001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS.
026 */
027package org.opends.server.types;
028
029import java.io.File;
030import java.io.FileNotFoundException;
031import java.io.IOException;
032import java.nio.file.Files;
033import java.nio.file.Path;
034import java.nio.file.attribute.AclFileAttributeView;
035import java.nio.file.attribute.PosixFileAttributeView;
036import java.nio.file.attribute.PosixFilePermission;
037import java.nio.file.attribute.PosixFilePermissions;
038import java.util.Set;
039
040import org.forgerock.i18n.LocalizableMessage;
041import org.forgerock.opendj.ldap.ResultCode;
042
043import static org.opends.messages.UtilityMessages.*;
044
045/**
046 * This class provides a mechanism for setting file permissions in a
047 * more abstract manner than is provided by the underlying operating
048 * system and/or filesystem.  It uses a traditional UNIX-style rwx/ugo
049 * representation for the permissions and converts them as necessary
050 * to the scheme used by the underlying platform.  It does not provide
051 * any mechanism for getting file permissions, nor does it provide any
052 * way of dealing with file ownership or ACLs.
053 */
054@org.opends.server.types.PublicAPI(
055     stability=org.opends.server.types.StabilityLevel.VOLATILE,
056     mayInstantiate=true,
057     mayExtend=false,
058     mayInvoke=true)
059public class FilePermission
060{
061  /**
062   * The bitmask that should be used for indicating whether a file is
063   * readable by its owner.
064   */
065  public static final int OWNER_READABLE = 0x0100;
066
067
068
069  /**
070   * The bitmask that should be used for indicating whether a file is
071   * writable by its owner.
072   */
073  public static final int OWNER_WRITABLE = 0x0080;
074
075
076
077  /**
078   * The bitmask that should be used for indicating whether a file is
079   * executable by its owner.
080   */
081  public static final int OWNER_EXECUTABLE = 0x0040;
082
083
084
085  /**
086   * The bitmask that should be used for indicating whether a file is
087   * readable by members of its group.
088   */
089  public static final int GROUP_READABLE = 0x0020;
090
091
092
093  /**
094   * The bitmask that should be used for indicating whether a file is
095   * writable by members of its group.
096   */
097  public static final int GROUP_WRITABLE = 0x0010;
098
099
100
101  /**
102   * The bitmask that should be used for indicating whether a file is
103   * executable by members of its group.
104   */
105  public static final int GROUP_EXECUTABLE = 0x0008;
106
107
108
109  /**
110   * The bitmask that should be used for indicating whether a file is
111   * readable by users other than the owner or group members.
112   */
113  public static final int OTHER_READABLE = 0x0004;
114
115
116
117  /**
118   * The bitmask that should be used for indicating whether a file is
119   * writable by users other than the owner or group members.
120   */
121  public static final int OTHER_WRITABLE = 0x0002;
122
123
124
125  /**
126   * The bitmask that should be used for indicating whether a file is
127   * executable by users other than the owner or group members.
128   */
129  public static final int OTHER_EXECUTABLE = 0x0001;
130
131
132
133  /** The encoded representation for this file permission. */
134  private int encodedPermission;
135
136
137
138  /**
139   * Creates a new file permission object with the provided encoded
140   * representation.
141   *
142   * @param  encodedPermission  The encoded representation for this
143   *                            file permission.
144   */
145  public FilePermission(int encodedPermission)
146  {
147    this.encodedPermission = encodedPermission;
148  }
149
150
151
152  /**
153   * Creates a new file permission with the specified rights for the
154   * file owner.  Users other than the owner will not have any rights.
155   *
156   * @param  ownerReadable    Indicates whether the owner should have
157   *                          the read permission.
158   * @param  ownerWritable    Indicates whether the owner should have
159   *                          the write permission.
160   * @param  ownerExecutable  Indicates whether the owner should have
161   *                          the execute permission.
162   */
163  public FilePermission(boolean ownerReadable, boolean ownerWritable,
164                        boolean ownerExecutable)
165  {
166    encodedPermission = 0x0000;
167
168    if (ownerReadable)
169    {
170      encodedPermission |= OWNER_READABLE;
171    }
172
173    if (ownerWritable)
174    {
175      encodedPermission |= OWNER_WRITABLE;
176    }
177
178    if (ownerExecutable)
179    {
180      encodedPermission |= OWNER_EXECUTABLE;
181    }
182  }
183
184
185
186  /**
187   * Creates a new file permission with the specified rights for the
188   * file owner, group members, and other users.
189   *
190   * @param  ownerReadable    Indicates whether the owner should have
191   *                          the read permission.
192   * @param  ownerWritable    Indicates whether the owner should have
193   *                          the write permission.
194   * @param  ownerExecutable  Indicates whether the owner should have
195   *                          the execute permission.
196   * @param  groupReadable    Indicates whether members of the file's
197   *                          group should have the read permission.
198   * @param  groupWritable    Indicates whether members of the file's
199   *                          group should have the write permission.
200   * @param  groupExecutable  Indicates whether members of the file's
201   *                          group should have the execute
202   *                          permission.
203   * @param  otherReadable    Indicates whether other users should
204   *                          have the read permission.
205   * @param  otherWritable    Indicates whether other users should
206   *                          have the write permission.
207   * @param  otherExecutable  Indicates whether other users should
208   *                          have the execute permission.
209   */
210  public FilePermission(boolean ownerReadable, boolean ownerWritable,
211                        boolean ownerExecutable,
212                        boolean groupReadable, boolean groupWritable,
213                        boolean groupExecutable,
214                        boolean otherReadable, boolean otherWritable,
215                        boolean otherExecutable)
216  {
217    encodedPermission = 0x0000;
218
219    if (ownerReadable)
220    {
221      encodedPermission |= OWNER_READABLE;
222    }
223
224    if (ownerWritable)
225    {
226      encodedPermission |= OWNER_WRITABLE;
227    }
228
229    if (ownerExecutable)
230    {
231      encodedPermission |= OWNER_EXECUTABLE;
232    }
233
234    if (groupReadable)
235    {
236      encodedPermission |= GROUP_READABLE;
237    }
238
239    if (groupWritable)
240    {
241      encodedPermission |= GROUP_WRITABLE;
242    }
243
244    if (groupExecutable)
245    {
246      encodedPermission |= GROUP_EXECUTABLE;
247    }
248
249    if (otherReadable)
250    {
251      encodedPermission |= OTHER_READABLE;
252    }
253
254    if (otherWritable)
255    {
256      encodedPermission |= OTHER_WRITABLE;
257    }
258
259    if (otherExecutable)
260    {
261      encodedPermission |= OTHER_EXECUTABLE;
262    }
263  }
264
265
266
267  /**
268   * Indicates whether this file permission includes the owner read
269   * permission.
270   *
271   * @return  <CODE>true</CODE> if this file permission includes the
272   *          owner read permission, or <CODE>false</CODE> if not.
273   */
274  public boolean isOwnerReadable()
275  {
276    return is(encodedPermission, OWNER_READABLE);
277  }
278
279
280
281  /**
282   * Indicates whether this file permission includes the owner write
283   * permission.
284   *
285   * @return  <CODE>true</CODE> if this file permission includes the
286   *          owner write permission, or <CODE>false</CODE> if not.
287   */
288  public boolean isOwnerWritable()
289  {
290    return is(encodedPermission, OWNER_WRITABLE);
291  }
292
293
294
295  /**
296   * Indicates whether this file permission includes the owner execute
297   * permission.
298   *
299   * @return  <CODE>true</CODE> if this file permission includes the
300   *          owner execute permission, or <CODE>false</CODE> if not.
301   */
302  public boolean isOwnerExecutable()
303  {
304    return is(encodedPermission, OWNER_EXECUTABLE);
305  }
306
307
308
309  /**
310   * Indicates whether this file permission includes the group read
311   * permission.
312   *
313   * @return  <CODE>true</CODE> if this file permission includes the
314   *          group read permission, or <CODE>false</CODE> if not.
315   */
316  public boolean isGroupReadable()
317  {
318    return is(encodedPermission, GROUP_READABLE);
319  }
320
321
322
323  /**
324   * Indicates whether this file permission includes the group write
325   * permission.
326   *
327   * @return  <CODE>true</CODE> if this file permission includes the
328   *          group write permission, or <CODE>false</CODE> if not.
329   */
330  public boolean isGroupWritable()
331  {
332    return is(encodedPermission, GROUP_WRITABLE);
333  }
334
335
336
337  /**
338   * Indicates whether this file permission includes the group execute
339   * permission.
340   *
341   * @return  <CODE>true</CODE> if this file permission includes the
342   *          group execute permission, or <CODE>false</CODE> if not.
343   */
344  public boolean isGroupExecutable()
345  {
346    return is(encodedPermission, GROUP_EXECUTABLE);
347  }
348
349
350
351  /**
352   * Indicates whether this file permission includes the other read
353   * permission.
354   *
355   * @return  <CODE>true</CODE> if this file permission includes the
356   *          other read permission, or <CODE>false</CODE> if not.
357   */
358  public boolean isOtherReadable()
359  {
360    return is(encodedPermission, OTHER_READABLE);
361  }
362
363
364
365  /**
366   * Indicates whether this file permission includes the other write
367   * permission.
368   *
369   * @return  <CODE>true</CODE> if this file permission includes the
370   *          other write permission, or <CODE>false</CODE> if not.
371   */
372  public boolean isOtherWritable()
373  {
374    return is(encodedPermission, OTHER_WRITABLE);
375  }
376
377
378
379  /**
380   * Indicates whether this file permission includes the other execute
381   * permission.
382   *
383   * @return  <CODE>true</CODE> if this file permission includes the
384   *          other execute permission, or <CODE>false</CODE> if not.
385   */
386  public boolean isOtherExecutable()
387  {
388    return is(encodedPermission, OTHER_EXECUTABLE);
389  }
390
391  private boolean is(int encodedPermissions, int permission)
392  {
393    return (encodedPermissions & permission) == permission;
394  }
395
396  /**
397   * Attempts to set the given permissions on the specified file.  If
398   * the underlying platform does not allow the full level of
399   * granularity specified in the permissions, then an attempt will be
400   * made to set them as closely as possible to the provided
401   * permissions, erring on the side of security.
402   *
403   * @param  f  The file to which the permissions should be applied.
404   * @param  p  The permissions to apply to the file.
405   *
406   * @return  <CODE>true</CODE> if the permissions (or the nearest
407   *          equivalent) were successfully applied to the specified
408   *          file, or <CODE>false</CODE> if was not possible to set
409   *          the permissions on the current platform.
410   *
411   * @throws  FileNotFoundException  If the specified file does not
412   *                                 exist.
413   *
414   * @throws  DirectoryException  If a problem occurs while trying to
415   *                              set the file permissions.
416   */
417  public static boolean setPermissions(File f, FilePermission p)
418         throws FileNotFoundException, DirectoryException
419  {
420    if (!f.exists())
421    {
422      throw new FileNotFoundException(ERR_FILEPERM_SET_NO_SUCH_FILE.get(f.getAbsolutePath()).toString());
423    }
424    Path filePath = f.toPath();
425    PosixFileAttributeView posix = Files.getFileAttributeView(filePath, PosixFileAttributeView.class);
426    if (posix != null)
427    {
428      StringBuilder posixMode = new StringBuilder();
429      toPOSIXString(p, posixMode, "", "", "");
430      Set<PosixFilePermission> perms = PosixFilePermissions.fromString(posixMode.toString());
431      try
432      {
433        Files.setPosixFilePermissions(filePath, perms);
434      }
435      catch (UnsupportedOperationException | ClassCastException | IOException | SecurityException ex)
436      {
437        throw new DirectoryException(ResultCode.OTHER, ERR_FILEPERM_SET_JAVA_EXCEPTION.get(f.getAbsolutePath()), ex);
438      }
439      return true;
440    }
441    return Files.getFileAttributeView(filePath, AclFileAttributeView.class) != null;
442  }
443
444
445
446  /**
447   * Retrieves a three-character string that is the UNIX mode for the
448   * provided file permission.  Each character of the string will be a
449   * numeric digit from zero through seven.
450   *
451   * @param  p  The permission to retrieve as a UNIX mode string.
452   *
453   * @return  The UNIX mode string for the provided permission.
454   */
455  public static String toUNIXMode(FilePermission p)
456  {
457    StringBuilder buffer = new StringBuilder(3);
458    toUNIXMode(buffer, p);
459    return buffer.toString();
460  }
461
462
463
464  /**
465   * Appends a three-character string that is the UNIX mode for the
466   * provided file permission to the given buffer.  Each character of
467   * the string will be a numeric digit from zero through seven.
468   *
469   * @param  buffer  The buffer to which the mode string should be
470   *                 appended.
471   * @param  p       The permission to retrieve as a UNIX mode string.
472   */
473  public static void toUNIXMode(StringBuilder buffer,
474                                FilePermission p)
475  {
476    byte modeByte = 0x00;
477    if (p.isOwnerReadable())
478    {
479      modeByte |= 0x04;
480    }
481    if (p.isOwnerWritable())
482    {
483      modeByte |= 0x02;
484    }
485    if (p.isOwnerExecutable())
486    {
487      modeByte |= 0x01;
488    }
489    buffer.append(modeByte);
490
491    modeByte = 0x00;
492    if (p.isGroupReadable())
493    {
494      modeByte |= 0x04;
495    }
496    if (p.isGroupWritable())
497    {
498      modeByte |= 0x02;
499    }
500    if (p.isGroupExecutable())
501    {
502      modeByte |= 0x01;
503    }
504    buffer.append(modeByte);
505
506    modeByte = 0x00;
507    if (p.isOtherReadable())
508    {
509      modeByte |= 0x04;
510    }
511    if (p.isOtherWritable())
512    {
513      modeByte |= 0x02;
514    }
515    if (p.isOtherExecutable())
516    {
517      modeByte |= 0x01;
518    }
519    buffer.append(modeByte);
520  }
521
522
523
524  /**
525   * Decodes the provided string as a UNIX mode and retrieves the
526   * corresponding file permission.  The mode string must contain
527   * three digits between zero and seven.
528   *
529   * @param  modeString  The string representation of the UNIX mode to
530   *                     decode.
531   *
532   * @return  The file permission that is equivalent to the given UNIX
533   *          mode.
534   *
535   * @throws  DirectoryException  If the provided string is not a
536   *                              valid three-digit UNIX mode.
537   */
538  public static FilePermission decodeUNIXMode(String modeString)
539         throws DirectoryException
540  {
541    if (modeString == null || modeString.length() != 3)
542    {
543      LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString);
544      throw new DirectoryException(ResultCode.OTHER, message);
545    }
546
547    int encodedPermission = 0x0000;
548    switch (modeString.charAt(0))
549    {
550      case '0':
551        break;
552      case '1':
553        encodedPermission |= OWNER_EXECUTABLE;
554        break;
555      case '2':
556        encodedPermission |= OWNER_WRITABLE;
557        break;
558      case '3':
559        encodedPermission |= OWNER_WRITABLE | OWNER_EXECUTABLE;
560        break;
561      case '4':
562        encodedPermission |= OWNER_READABLE;
563        break;
564      case '5':
565         encodedPermission |= OWNER_READABLE | OWNER_EXECUTABLE;
566        break;
567      case '6':
568        encodedPermission |= OWNER_READABLE | OWNER_WRITABLE;
569        break;
570      case '7':
571        encodedPermission |= OWNER_READABLE | OWNER_WRITABLE |
572                             OWNER_EXECUTABLE;
573        break;
574      default:
575      LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString);
576      throw new DirectoryException(ResultCode.OTHER, message);
577    }
578
579    switch (modeString.charAt(1))
580    {
581      case '0':
582        break;
583      case '1':
584        encodedPermission |= GROUP_EXECUTABLE;
585        break;
586      case '2':
587        encodedPermission |= GROUP_WRITABLE;
588        break;
589      case '3':
590        encodedPermission |= GROUP_WRITABLE | GROUP_EXECUTABLE;
591        break;
592      case '4':
593        encodedPermission |= GROUP_READABLE;
594        break;
595      case '5':
596         encodedPermission |= GROUP_READABLE | GROUP_EXECUTABLE;
597        break;
598      case '6':
599        encodedPermission |= GROUP_READABLE | GROUP_WRITABLE;
600        break;
601      case '7':
602        encodedPermission |= GROUP_READABLE | GROUP_WRITABLE |
603                             GROUP_EXECUTABLE;
604        break;
605      default:
606      LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString);
607      throw new DirectoryException(ResultCode.OTHER, message);
608    }
609
610    switch (modeString.charAt(2))
611    {
612      case '0':
613        break;
614      case '1':
615        encodedPermission |= OTHER_EXECUTABLE;
616        break;
617      case '2':
618        encodedPermission |= OTHER_WRITABLE;
619        break;
620      case '3':
621        encodedPermission |= OTHER_WRITABLE | OTHER_EXECUTABLE;
622        break;
623      case '4':
624        encodedPermission |= OTHER_READABLE;
625        break;
626      case '5':
627         encodedPermission |= OTHER_READABLE | OTHER_EXECUTABLE;
628        break;
629      case '6':
630        encodedPermission |= OTHER_READABLE | OTHER_WRITABLE;
631        break;
632      case '7':
633        encodedPermission |= OTHER_READABLE | OTHER_WRITABLE |
634                             OTHER_EXECUTABLE;
635        break;
636      default:
637      LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString);
638      throw new DirectoryException(ResultCode.OTHER, message);
639    }
640
641    return new FilePermission(encodedPermission);
642  }
643
644
645
646  /**
647   * Build a file permissions string in the "rwx" form expected by NIO,
648   * but with optional prefix strings before each three character block.
649   * <p>
650   * For example: "rwxr-xrw-" and "Owner=rwx, Group=r-x", Other=rw-".
651   *
652   * @param p      The file permissions to use.
653   * @param buffer The buffer being appended to.
654   * @param owner  The owner prefix, must not be null.
655   * @param group  The group prefix, must not be null.
656   * @param other  The other prefix, must not be null.
657   */
658  private static void toPOSIXString(FilePermission p, StringBuilder buffer,
659      String owner, String group, String other)
660  {
661    buffer.append(owner);
662    buffer.append(p.isOwnerReadable() ? "r" : "-");
663    buffer.append(p.isOwnerWritable() ? "w" : "-");
664    buffer.append(p.isOwnerExecutable() ? "x" : "-");
665
666    buffer.append(group);
667    buffer.append(p.isGroupReadable() ? "r" : "-");
668    buffer.append(p.isGroupWritable() ? "w" : "-");
669    buffer.append(p.isGroupExecutable() ? "x" : "-");
670
671    buffer.append(other);
672    buffer.append(p.isOtherReadable() ? "r" : "-");
673    buffer.append(p.isOtherWritable() ? "w" : "-");
674    buffer.append(p.isOtherExecutable() ? "x" : "-");
675  }
676
677
678
679  /**
680   * Retrieves a string representation of this file permission.
681   *
682   * @return  A string representation of this file permission.
683   */
684  @Override
685  public String toString()
686  {
687    StringBuilder buffer = new StringBuilder();
688    toString(buffer);
689    return buffer.toString();
690  }
691
692
693
694  /**
695   * Appends a string representation of this file permission to the
696   * given buffer.
697   *
698   * @param  buffer  The buffer to which the data should be appended.
699   */
700  public void toString(StringBuilder buffer)
701  {
702    toPOSIXString(this, buffer, "Owner=", ", Group=", ", Other=");
703  }
704}
705