001/* 002 * Copyright (c) 2006-2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Bogdan Stefanescu 011 * Florent Guillaume 012 */ 013package org.nuxeo.ecm.core.api.security; 014 015import java.io.Serializable; 016import java.util.Calendar; 017import java.util.GregorianCalendar; 018import java.util.HashMap; 019import java.util.Map; 020 021import org.apache.commons.lang.StringUtils; 022 023/** 024 * Access control entry, assigning a permission to a user. 025 * <p> 026 * Optionally, the assignment can be denied instead of being granted. 027 */ 028public final class ACE implements Serializable, Cloneable { 029 030 public enum Status { 031 PENDING, EFFECTIVE, ARCHIVED; 032 } 033 034 /** 035 * An ACE that blocks all permissions for everyone. 036 * 037 * @since 6.0 038 */ 039 public static final ACE BLOCK = new ACE(SecurityConstants.EVERYONE, SecurityConstants.EVERYTHING, false); 040 041 private final String username; 042 043 private final String permission; 044 045 private final boolean isGranted; 046 047 private Calendar begin; 048 049 private Calendar end; 050 051 private String creator; 052 053 private Map<String, Serializable> contextData = new HashMap<>(); 054 055 /** 056 * Create an ACE from an id. 057 * 058 * @since 7.4 059 */ 060 public static ACE fromId(String aceId) { 061 if (aceId == null) { 062 return null; 063 } 064 065 String[] parts = aceId.split(":"); 066 if (parts.length < 3) { 067 throw new IllegalArgumentException(String.format("Invalid ACE id: %s", aceId)); 068 } 069 070 String username = parts[0]; 071 String permission = parts[1]; 072 boolean isGranted = Boolean.valueOf(parts[2]); 073 074 ACEBuilder builder = ACE.builder(username, permission).isGranted(isGranted); 075 076 if (parts.length >= 4 && StringUtils.isNotBlank(parts[3])) { 077 builder.creator(parts[3]); 078 } 079 080 if (parts.length >= 5 && StringUtils.isNotBlank(parts[4])) { 081 Calendar begin = new GregorianCalendar(); 082 begin.setTimeInMillis(Long.valueOf(parts[4])); 083 builder.begin(begin); 084 } 085 086 if (parts.length >= 6 && StringUtils.isNotBlank(parts[5])) { 087 Calendar end = new GregorianCalendar(); 088 end.setTimeInMillis(Long.valueOf(parts[5])); 089 builder.end(end); 090 } 091 092 return builder.build(); 093 } 094 095 public ACE() { 096 this(null, null, false); 097 } 098 099 /** 100 * Constructs an ACE for a given username and permission, and specifies whether to grant or deny it. 101 */ 102 public ACE(String username, String permission, boolean isGranted) { 103 this(username, permission, isGranted, null, null, null, null); 104 } 105 106 /** 107 * Constructs an ACE for a given username and permission. 108 * <p> 109 * The ACE is granted. 110 * 111 * @since 6.0 112 */ 113 public ACE(String username, String permission) { 114 this(username, permission, true); 115 } 116 117 /** 118 * Constructs an ACE for a given username, permission, specifying whether to grant or deny it, creator user, begin 119 * and end date. 120 * 121 * @since 7.4 122 */ 123 ACE(String username, String permission, boolean isGranted, String creator, Calendar begin, Calendar end, 124 Map<String, Serializable> contextData) { 125 this.username = username; 126 this.permission = permission; 127 this.isGranted = isGranted; 128 this.creator = creator; 129 this.begin = begin; 130 this.end = end; 131 if (contextData != null) { 132 this.contextData = new HashMap<>(contextData); 133 } 134 135 if (begin != null && end != null) { 136 if (begin.after(end)) { 137 throw new IllegalArgumentException("'begin' date cannot be after 'end' date"); 138 } 139 } 140 } 141 142 /** 143 * Returns this ACE id. 144 * <p> 145 * This id is unique inside a given ACL. 146 * 147 * @since 7.4 148 */ 149 public String getId() { 150 StringBuilder sb = new StringBuilder(); 151 sb.append(username); 152 sb.append(':'); 153 sb.append(permission); 154 sb.append(':'); 155 sb.append(isGranted); 156 157 sb.append(':'); 158 if (creator != null) { 159 sb.append(creator); 160 } 161 162 sb.append(':'); 163 if (begin != null) { 164 sb.append(begin.getTimeInMillis()); 165 } 166 167 sb.append(':'); 168 if (end != null) { 169 sb.append(end.getTimeInMillis()); 170 } 171 172 return sb.toString(); 173 } 174 175 public String getUsername() { 176 return username; 177 } 178 179 public String getPermission() { 180 return permission; 181 } 182 183 /** 184 * Checks if this privilege is granted. 185 * 186 * @return true if the privilege is granted 187 */ 188 public boolean isGranted() { 189 return isGranted; 190 } 191 192 /** 193 * Checks if this privilege is denied. 194 * 195 * @return true if privilege is denied 196 */ 197 public boolean isDenied() { 198 return !isGranted; 199 } 200 201 public Calendar getBegin() { 202 return begin; 203 } 204 205 public void setBegin(Calendar begin) { 206 this.begin = begin; 207 } 208 209 public Calendar getEnd() { 210 return end; 211 } 212 213 public void setEnd(Calendar end) { 214 this.end = end; 215 } 216 217 public String getCreator() { 218 return creator; 219 } 220 221 public void setCreator(String creator) { 222 this.creator = creator; 223 } 224 225 /** 226 * Returns the status of this ACE. 227 * 228 * @since 7.4 229 */ 230 public Status getStatus() { 231 Status status = Status.EFFECTIVE; 232 Calendar now = new GregorianCalendar(); 233 if (begin != null && now.before(begin)) { 234 status = Status.PENDING; 235 } 236 if (end != null && now.after(end)) { 237 status = Status.ARCHIVED; 238 } 239 return status; 240 } 241 242 /** 243 * Returns a Long value of this ACE status. 244 * <p> 245 * It returns {@code null} if there is no begin and end date, which means the ACE is effective. Otherwise, it 246 * returns 0 for PENDING, 1 for EFFECTIVE and 2 for ARCHIVED. 247 * 248 * @since 7.4 249 */ 250 public Long getLongStatus() { 251 if (begin == null && end == null) { 252 return null; 253 } 254 return Long.valueOf(getStatus().ordinal()); 255 } 256 257 public boolean isEffective() { 258 return getStatus() == Status.EFFECTIVE; 259 } 260 261 public boolean isPending() { 262 return getStatus() == Status.PENDING; 263 } 264 265 public boolean isArchived() { 266 return getStatus() == Status.ARCHIVED; 267 } 268 269 public Serializable getContextData(String key) { 270 return contextData.get(key); 271 } 272 273 public void putContextData(String key, Serializable value) { 274 contextData.put(key, value); 275 } 276 277 @Override 278 public boolean equals(Object obj) { 279 if (this == obj) { 280 return true; 281 } 282 if (obj instanceof ACE) { 283 ACE ace = (ACE) obj; 284 // check Calendars without handling timezone 285 boolean beginEqual = ace.begin == null && begin == null 286 || !(ace.begin == null || begin == null) && ace.begin.getTimeInMillis() == begin.getTimeInMillis(); 287 boolean endEqual = ace.end == null && end == null 288 || !(ace.end == null || end == null) && ace.end.getTimeInMillis() == end.getTimeInMillis(); 289 boolean creatorEqual = ace.creator != null ? ace.creator.equals(creator) : creator == null; 290 boolean usernameEqual = ace.username != null ? ace.username.equals(username) : username == null; 291 return ace.isGranted == isGranted && usernameEqual && ace.permission.equals(permission) && creatorEqual 292 && beginEqual && endEqual; 293 } 294 return super.equals(obj); 295 } 296 297 @Override 298 public int hashCode() { 299 int hash = 17; 300 hash = hash * 37 + (isGranted ? 1 : 0); 301 hash = hash * 37 + username.hashCode(); 302 hash = creator != null ? hash * 37 + creator.hashCode() : hash; 303 hash = begin != null ? hash * 37 + begin.hashCode() : hash; 304 hash = end != null ? hash * 37 + end.hashCode() : hash; 305 return hash * 37 + permission.hashCode(); 306 } 307 308 @Override 309 public String toString() { 310 StringBuilder sb = new StringBuilder(); 311 sb.append(getClass().getSimpleName()); 312 sb.append('('); 313 sb.append("username=").append(username); 314 sb.append(", "); 315 sb.append("permission=" + permission); 316 sb.append(", "); 317 sb.append("isGranted=" + isGranted); 318 sb.append(", "); 319 sb.append("creator=" + creator); 320 sb.append(", "); 321 sb.append("begin=" + (begin != null ? begin.getTimeInMillis() : null)); 322 sb.append(", "); 323 sb.append("end=" + (end != null ? end.getTimeInMillis() : null)); 324 sb.append(')'); 325 return sb.toString(); 326 } 327 328 @Override 329 public Object clone() { 330 return new ACE(username, permission, isGranted, creator, begin, end, contextData); 331 } 332 333 public static ACEBuilder builder(String username, String permission) { 334 return new ACEBuilder(username, permission); 335 } 336 337 public static class ACEBuilder { 338 339 private String username; 340 341 private String permission; 342 343 private boolean isGranted = true; 344 345 private Calendar begin; 346 347 private Calendar end; 348 349 private String creator; 350 351 private Map<String, Serializable> contextData; 352 353 public ACEBuilder(String username, String permission) { 354 this.username = username; 355 this.permission = permission; 356 } 357 358 public ACEBuilder isGranted(boolean isGranted) { 359 this.isGranted = isGranted; 360 return this; 361 } 362 363 public ACEBuilder begin(Calendar begin) { 364 this.begin = begin; 365 return this; 366 } 367 368 public ACEBuilder end(Calendar end) { 369 this.end = end; 370 return this; 371 } 372 373 public ACEBuilder creator(String creator) { 374 this.creator = creator; 375 return this; 376 } 377 378 public ACEBuilder contextData(Map<String, Serializable> contextData) { 379 this.contextData = contextData; 380 return this; 381 } 382 383 public ACE build() { 384 return new ACE(username, permission, isGranted, creator, begin, end, contextData); 385 } 386 } 387 388}