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