1 /* 2 * Copyright 2008 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package org.mockftpserver.fake; 17 18 import org.mockftpserver.core.util.Assert; 19 import org.mockftpserver.fake.filesystem.FileSystemEntry; 20 import org.mockftpserver.fake.filesystem.Permissions; 21 22 import java.util.List; 23 24 /** 25 * Represents a single user account on the server, including the username, password, home 26 * directory, list of groups to which this user belongs, and default permissions applied to 27 * newly-created files and directories. 28 * <p/> 29 * The <code>username</code> and <code>homeDirectory</code> property must be non-null 30 * and non-empty. The <code>homeDirectory</code> property must also match the name of an existing 31 * directory within the file system configured for the <code>FakeFtpServer</code>. 32 * <p/> 33 * The group name applied to newly created files/directories is determined by the <code>groups</code> property. 34 * If null or empty, then the default group name ("users") is used. Otherwise, the first value in the 35 * <code>groups</code> List is used. The <code>groups</code> property defaults to an empty List. 36 * <p/> 37 * The default value for <code>defaultPermissionsForNewFile</code> is read and write permissions for 38 * all (user/group/world). The default value for <code>defaultPermissionsForNewDirectory</code> is read, 39 * write and execute permissions for all (user/group/world). 40 * <p/> 41 * The <code>isValidPassword()</code> method returns true if the specified password matches 42 * the password value configured for this user account. This implementation uses the 43 * <code>isEquals()</code> method to compare passwords. 44 * <p/> 45 * If you want to provide a custom comparison, for instance using encrypted passwords, you can 46 * subclass this class and override the <code>comparePassword()</code> method to provide your own 47 * custom implementation. 48 * <p/> 49 * If the <code>passwordCheckedDuringValidation</code> property is set to false, then the password 50 * value is ignored, and the <code>isValidPassword()</code> method just returns <code<true</code>. 51 * <p/> 52 * The <code>accountRequiredForLogin</code> property defaults to false. If it is set to true, then 53 * it is expected that the login for this account will require an ACCOUNT (ACCT) command after the 54 * PASSWORD (PASS) command is completed. 55 */ 56 public class UserAccount { 57 58 public static final String DEFAULT_USER = "system"; 59 public static final String DEFAULT_GROUP = "users"; 60 public static final Permissions DEFAULT_PERMISSIONS_FOR_NEW_FILE = new Permissions("rw-rw-rw-"); 61 public static final Permissions DEFAULT_PERMISSIONS_FOR_NEW_DIRECTORY = Permissions.ALL; 62 63 private String username; 64 private String password; 65 private String homeDirectory; 66 private List groups; 67 private boolean passwordRequiredForLogin = true; 68 private boolean passwordCheckedDuringValidation = true; 69 private boolean accountRequiredForLogin = false; 70 private Permissions defaultPermissionsForNewFile = DEFAULT_PERMISSIONS_FOR_NEW_FILE; 71 private Permissions defaultPermissionsForNewDirectory = DEFAULT_PERMISSIONS_FOR_NEW_DIRECTORY; 72 73 74 /** 75 * Construct a new uninitialized instance. 76 */ 77 public UserAccount() { 78 } 79 80 /** 81 * Construct a new initialized instance. 82 * 83 * @param username - the user name 84 * @param password - the password 85 * @param homeDirectory - the home directory 86 */ 87 public UserAccount(String username, String password, String homeDirectory) { 88 setUsername(username); 89 setPassword(password); 90 setHomeDirectory(homeDirectory); 91 } 92 93 public String getUsername() { 94 return username; 95 } 96 97 public void setUsername(String username) { 98 this.username = username; 99 } 100 101 public String getPassword() { 102 return password; 103 } 104 105 public void setPassword(String password) { 106 this.password = password; 107 } 108 109 public String getHomeDirectory() { 110 return homeDirectory; 111 } 112 113 public void setHomeDirectory(String homeDirectory) { 114 this.homeDirectory = homeDirectory; 115 } 116 117 public List getGroups() { 118 return groups; 119 } 120 121 public void setGroups(List groups) { 122 this.groups = groups; 123 } 124 125 public boolean isPasswordRequiredForLogin() { 126 return passwordRequiredForLogin; 127 } 128 129 public void setPasswordRequiredForLogin(boolean passwordRequiredForLogin) { 130 this.passwordRequiredForLogin = passwordRequiredForLogin; 131 } 132 133 public boolean isPasswordCheckedDuringValidation() { 134 return passwordCheckedDuringValidation; 135 } 136 137 public void setPasswordCheckedDuringValidation(boolean passwordCheckedDuringValidation) { 138 this.passwordCheckedDuringValidation = passwordCheckedDuringValidation; 139 } 140 141 public boolean isAccountRequiredForLogin() { 142 return accountRequiredForLogin; 143 } 144 145 public void setAccountRequiredForLogin(boolean accountRequiredForLogin) { 146 this.accountRequiredForLogin = accountRequiredForLogin; 147 } 148 149 public Permissions getDefaultPermissionsForNewFile() { 150 return defaultPermissionsForNewFile; 151 } 152 153 public void setDefaultPermissionsForNewFile(Permissions defaultPermissionsForNewFile) { 154 this.defaultPermissionsForNewFile = defaultPermissionsForNewFile; 155 } 156 157 public Permissions getDefaultPermissionsForNewDirectory() { 158 return defaultPermissionsForNewDirectory; 159 } 160 161 public void setDefaultPermissionsForNewDirectory(Permissions defaultPermissionsForNewDirectory) { 162 this.defaultPermissionsForNewDirectory = defaultPermissionsForNewDirectory; 163 } 164 165 /** 166 * Return the name of the primary group to which this user belongs. If this account has no associated 167 * groups set, then this method returns the <code>DEFAULT_GROUP</code>. Otherwise, this method 168 * returns the first group name in the <code>groups</code> list. 169 * 170 * @return the name of the primary group for this user 171 */ 172 public String getPrimaryGroup() { 173 return (groups == null || groups.isEmpty()) ? DEFAULT_GROUP : (String) groups.get(0); 174 } 175 176 /** 177 * Return true if the specified password is the correct, valid password for this user account. 178 * This implementation uses standard (case-sensitive) String comparison. Subclasses can provide 179 * custom comparison behavior, for instance using encrypted password values, by overriding this 180 * method. 181 * 182 * @param password - the password to compare against the configured value 183 * @return true if the password is correct and valid 184 * @throws org.mockftpserver.core.util.AssertFailedException 185 * - if the username property is null 186 */ 187 public boolean isValidPassword(String password) { 188 Assert.notNullOrEmpty(username, "username"); 189 return !passwordCheckedDuringValidation || comparePassword(password); 190 } 191 192 /** 193 * @return true if this UserAccount object is valid; i.e. if the homeDirectory is non-null and non-empty. 194 */ 195 public boolean isValid() { 196 return homeDirectory != null && homeDirectory.length() > 0; 197 } 198 199 /** 200 * @return the String representation of this object 201 */ 202 public String toString() { 203 return "UserAccount[username=" + username + "; password=" + password + "; homeDirectory=" 204 + homeDirectory + "; passwordRequiredForLogin=" + passwordRequiredForLogin + "]"; 205 } 206 207 /** 208 * Return true if this user has read access to the file/directory represented by the specified FileSystemEntry object. 209 * 210 * @param entry - the FileSystemEntry representing the file or directory 211 * @return true if this use has read access 212 */ 213 public boolean canRead(FileSystemEntry entry) { 214 Permissions permissions = entry.getPermissions(); 215 if (permissions == null) { 216 return true; 217 } 218 219 if (equalOrBothNull(username, entry.getOwner())) { 220 return permissions.canUserRead(); 221 } 222 if (groups != null && groups.contains(entry.getGroup())) { 223 return permissions.canGroupRead(); 224 } 225 return permissions.canWorldRead(); 226 } 227 228 /** 229 * Return true if this user has write access to the file/directory represented by the specified FileSystemEntry object. 230 * 231 * @param entry - the FileSystemEntry representing the file or directory 232 * @return true if this use has write access 233 */ 234 public boolean canWrite(FileSystemEntry entry) { 235 Permissions permissions = entry.getPermissions(); 236 if (permissions == null) { 237 return true; 238 } 239 240 if (equalOrBothNull(username, entry.getOwner())) { 241 return permissions.canUserWrite(); 242 } 243 if (groups != null && groups.contains(entry.getGroup())) { 244 return permissions.canGroupWrite(); 245 } 246 return permissions.canWorldWrite(); 247 } 248 249 /** 250 * Return true if this user has execute access to the file/directory represented by the specified FileSystemEntry object. 251 * 252 * @param entry - the FileSystemEntry representing the file or directory 253 * @return true if this use has execute access 254 */ 255 public boolean canExecute(FileSystemEntry entry) { 256 Permissions permissions = entry.getPermissions(); 257 if (permissions == null) { 258 return true; 259 } 260 261 if (equalOrBothNull(username, entry.getOwner())) { 262 return permissions.canUserExecute(); 263 } 264 if (groups != null && groups.contains(entry.getGroup())) { 265 return permissions.canGroupExecute(); 266 } 267 return permissions.canWorldExecute(); 268 } 269 270 /** 271 * Return true if the specified password matches the password configured for this user account. 272 * This implementation uses standard (case-sensitive) String comparison. Subclasses can provide 273 * custom comparison behavior, for instance using encrypted password values, by overriding this 274 * method. 275 * 276 * @param password - the password to compare against the configured value 277 * @return true if the passwords match 278 */ 279 protected boolean comparePassword(String password) { 280 return password != null && password.equals(this.password); 281 } 282 283 /** 284 * Return true only if both Strings are null or they are equal (have the same contents). 285 * 286 * @param string1 - the first String 287 * @param string2 - the second String 288 * @return true if both are null or both are equal 289 */ 290 protected boolean equalOrBothNull(String string1, String string2) { 291 return (string1 == null && string2 == null) || (string1 != null && string1.equals(string2)); 292 } 293 294 }