Project

General

Profile

Download (10.8 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2012 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.cdm.persistence.permission;
10

    
11
import java.util.EnumSet;
12
import java.util.HashMap;
13
import java.util.Map;
14
import java.util.UUID;
15
import java.util.regex.Matcher;
16
import java.util.regex.Pattern;
17

    
18
import org.apache.commons.lang.builder.HashCodeBuilder;
19
import org.apache.commons.lang3.StringUtils;
20
import org.apache.log4j.Logger;
21
import org.springframework.security.access.ConfigAttribute;
22
import org.springframework.security.core.GrantedAuthority;
23

    
24
import eu.etaxonomy.cdm.model.common.CdmBase;
25
import eu.etaxonomy.cdm.model.permission.CRUD;
26
import eu.etaxonomy.cdm.model.permission.GrantedAuthorityImpl;
27
import eu.etaxonomy.cdm.model.permission.Operation;
28
import eu.etaxonomy.cdm.model.permission.PermissionClass;
29

    
30
/**
31
 * A <code>CdmAuthority</code> consists basically of two parts which are separated
32
 * by a dot character '.'.
33
 *
34
 * <ul>
35
 * <li><code>permissionClass</code>: an {@link PermissionClass} instance with represents a cdm
36
 * type or a part of the cdm type hierarchy. The className is always represented
37
 * as an upper case string.</li>
38
 * <li><code>property</code>: The <code>CdmAuthority</code> only applies to instances
39
 * which satisfy the specified property. Interpretation is up to type specific voters.</li>
40
 * <li><code>operation</code>: A string enclosed in brackets <code>[]</code>
41
 * which specifies one {@link Operation} or
42
 * multiple on that set of cdm types. Multiple {@link Operation} must be comma
43
 * separated.</li>
44
 * <li><code>targetUuid</code>: The <code>operation</code> may be restricted to a specific cdm entity by adding
45
 * the entity uuid to the <code>operation</code>. The uuid string is enclosed in curly brackets '<code>{</code>'
46
 * , '<code>}</code>' and appended to the end of the <code>operation</code>.</li>
47
 * </ul>
48
 *
49
 * <h3>Examples for permissionStrings</h3>
50
 *
51
 * <pre>
52
 * TAXONBASE.[CREATE]
53
 * TAXONBASE.[READ]
54
 * TAXONBASE.[UPDATE]
55
 * TAXONBASE.[DELETE]
56
 * DESCRIPTIONBASE.[UPDATE]
57
 * DESCRIPTIONBASE.[CREATE,UPDATE,DELETE,READ]
58
 * DESCRIPTIONELEMENTBASE(Ecology).[UPDATE]
59
 * TAXONNODE.[UPDATE]{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}
60
 * </pre>
61
 *
62
 * The method {@link #getPermissionString(String)} parses a full authority and returns  permissionString and
63
 * the {@link CdmAuthority} from the <code>authority</code>.
64
 *
65
 *
66
 * @author k.luther
67
 * @author Andreas Kohlbecker
68
 */
69
public class CdmAuthority implements GrantedAuthority, ConfigAttribute, IGrantedAuthorityConverter {
70

    
71
    private static final long serialVersionUID = -41894743719582595L;
72
    public static final Logger logger = Logger.getLogger(CdmAuthority.class);
73

    
74
    private static Map<String, CdmAuthority> grantedAuthorityCache = new HashMap<>();
75

    
76
    private PermissionClass permissionClass;
77
    private String property;
78
    // Making sure that operation is always initialized, for both
79
    // - the string representation to have a '[]'
80
    // - and the object representation to never be null (with check in constructors)
81
    private EnumSet<CRUD> operation = EnumSet.noneOf(CRUD.class);
82
    private UUID targetUuid;
83

    
84
    public CdmAuthority(CdmBase targetDomainObject, EnumSet<CRUD> operation){
85

    
86
        this.permissionClass = PermissionClass.getValueOf(targetDomainObject);
87
        this.property = null;
88
        if(operation != null) {
89
        	this.operation = operation;
90
        }
91
        if(targetDomainObject.getUuid() == null){
92
            throw new NullPointerException("UUID of targetDomainObject is null. CDM entities need to be saved prior using this function");
93
        }
94
        this.targetUuid = targetDomainObject.getUuid();
95
    }
96

    
97
     public CdmAuthority(CdmBase targetDomainObject, String property, EnumSet<CRUD> operation){
98
         this.permissionClass = PermissionClass.getValueOf(targetDomainObject);
99
          this.property = property;
100
          if(operation != null) {
101
              this.operation = operation;
102
          }
103
          this.targetUuid = targetDomainObject.getUuid();
104
      }
105

    
106
     public CdmAuthority(Class<? extends CdmBase> targetDomainType, String property, EnumSet<CRUD> operation, UUID uuid){
107
         this.permissionClass = PermissionClass.getValueOf(targetDomainType);
108
          this.property = property;
109
          if(operation != null) {
110
              this.operation = operation;
111
          }
112
          this.targetUuid = uuid;
113
      }
114

    
115

    
116
    public CdmAuthority(PermissionClass permissionClass, String property, EnumSet<CRUD> operation, UUID uuid){
117
        this.permissionClass = permissionClass;
118
        this.property = property;
119
        if(operation != null) {
120
        	this.operation = operation;
121
        }
122
        this.targetUuid = uuid;
123
    }
124

    
125
    public CdmAuthority(PermissionClass permissionClass, EnumSet<CRUD> operation){
126
        this.permissionClass = permissionClass;
127
        if(operation != null) {
128
            this.operation = operation;
129
        }
130
    }
131

    
132
    protected CdmAuthority (String authority) throws CdmAuthorityParsingException{
133

    
134
        String[] tokens = parse(authority);
135
        // className must never be null
136

    
137
        try {
138
            permissionClass = PermissionClass.valueOf(tokens[0]);
139
        } catch (IllegalArgumentException e) {
140
            throw new CdmAuthorityParsingException(authority);
141
        }
142
        property = tokens[1];
143

    
144
        if(tokens[2] != null){
145
            try {
146
                operation = Operation.fromString(tokens[2]);
147
            } catch (IllegalArgumentException e) {
148
                logger.warn("cannot parse Operation " + tokens[2]);
149
                throw new CdmAuthorityParsingException(authority);
150
            }
151
        }
152
        if(tokens[3] != null){
153
            targetUuid = UUID.fromString(tokens[3]);
154
        }
155
    }
156

    
157
    public PermissionClass getPermissionClass(){
158
        return permissionClass;
159
    }
160

    
161
    public String getProperty(){
162
        return property;
163
    }
164

    
165
    public EnumSet<CRUD> getOperation(){
166
        return operation;
167
    }
168

    
169
    public void setOperation(EnumSet<CRUD> operation) {
170
        this.operation = operation;
171
    }
172

    
173
    public UUID getTargetUUID(){
174
        return targetUuid;
175
    }
176

    
177
    public boolean hasTargetUuid() {
178
        return targetUuid != null;
179
    }
180

    
181
    public boolean hasProperty() {
182
        return property != null;
183
    }
184

    
185
    /**
186
     * Parses the given <code>authority</code> and returns an array of tokens.
187
     * The array has a length of four elements whereas the elements can be null.
188
     * The elements in the array correspond to the fields of {@link CdmAuthority}:
189
     * <ol>
190
     * <li>{@link CdmAuthority#permissionClass}</li>
191
     * <li>{@link CdmAuthority#property}</li>
192
     * <li>{@link CdmAuthority#operation}</li>
193
     * <li>{@link CdmAuthority#targetUuid}</li>
194
     * </ol>
195
     * @param authority
196
     * @return an array of tokens
197
     * @throws CdmAuthorityParsingException
198
     */
199
    protected String[] parse(String authority) throws CdmAuthorityParsingException {
200
        //
201
        // regex pattern explained:
202
        //  (\\w*)             -> classname
203
        //  (?:\\((\\D*)\\))?  -> (property)
204
        //  \\.?               -> .
205
        //  (?:\\[(\\D*)\\])(?:\\{([\\da-z\\-]+)\\})? -> Permission and targetUuid
206
        //
207
        String regex = "(\\w*)(?:\\((\\D*)\\))?\\.?(?:\\[(\\D*)\\])?(?:\\{([\\da-z\\-]+)\\})?";
208
        Pattern pattern = Pattern.compile(regex);
209
        String[] tokens = new String[4];
210
        logger.debug("parsing '" + authority + "'");
211
        Matcher m = pattern.matcher(authority);
212

    
213
        if (m.find() && m.groupCount() == 4 ) {
214
            for (int i = 0; i < m.groupCount(); i++) {
215
                tokens[i] = m.group(i+1);
216
                // normalize empty strings to null
217
                if(tokens[i] != null && tokens[i].length() == 0){
218
                    tokens[i] = null;
219
                }
220
                logger.trace("[" + i + "]: " + tokens[i]+ "\n");
221
            }
222
        } else {
223
            logger.debug("no match");
224
            throw new CdmAuthorityParsingException("Unsupported authority string: '" + authority + "'");
225
        }
226

    
227
        return tokens;
228
    }
229

    
230
    /**
231
     * {@inheritDoc}
232
     *
233
     * same as {@link #toString()} and  {@link #getAttribute()}
234
     */
235
    @Override
236
    public String getAuthority() {
237
        return toString();
238
    }
239

    
240
    /**
241
     * {@inheritDoc}
242
     *
243
     * same as {@link #toString()} and  {@link #getAuthority()}
244
     */
245
    @Override
246
    public String getAttribute() {
247
        return toString();
248
    }
249

    
250
    @Override
251
    public String toString() {
252
        StringBuilder sb = new StringBuilder();
253
        sb.append(permissionClass.toString());
254
        if(property != null){
255
            sb.append('(').append(property).append(')');
256
        }
257
        sb.append('.').append(operationsToString());
258
        if(targetUuid != null){
259
            sb.append('{').append(targetUuid.toString()).append('}');
260
        }
261
        return sb.toString() ;
262
    }
263

    
264
    @Override
265
    public boolean equals(Object o){
266
        if(o != null && o instanceof CdmAuthority){
267
            return toString().equals(((CdmAuthority)o).toString());
268
        }
269
        return false;
270
    }
271

    
272
    @Override
273
    public int hashCode() {
274
        return new HashCodeBuilder()
275
                .append(this.permissionClass)
276
                .append(this.property)
277
                .append(this.operation)
278
                .append(this.targetUuid)
279
                .toHashCode();
280
    }
281

    
282
    protected String operationsToString() {
283
        String[] opsstr = new String[operation.size()];
284
        int i = 0;
285
        for(CRUD crud : operation){
286
            opsstr[i++] = crud.name();
287
        }
288
        String asString = StringUtils.join(opsstr, ",");
289
        return "[" + asString + "]";
290
    }
291

    
292
    /**
293
     * Constructs a new CdmAuthority by parsing the authority string.
294
     * For details on the syntax please refer to the class
295
     * documentation above.
296
     * <p>
297
     * This method is mainly used by the permission voters ({@link CdmPermissionVoter)}.
298
     * In order to improve the voting process this method is caching the <code>CdmAuthority</code>
299
     * instances per <code>GrantedAuthority</code> string in a map.
300
     *
301
     * @param authority
302
     * @throws CdmAuthorityParsingException
303
     */
304
    public static CdmAuthority fromGrantedAuthority(GrantedAuthority authority) throws CdmAuthorityParsingException {
305
        CdmAuthority cdmAuthority = grantedAuthorityCache.get(authority.getAuthority());
306
        if(cdmAuthority == null){
307
            cdmAuthority = new CdmAuthority(authority.getAuthority());
308
        }
309
        return cdmAuthority;
310
//        return  new CdmAuthority(authority.getAuthority());
311
    }
312

    
313

    
314
    @Override
315
    public GrantedAuthorityImpl asNewGrantedAuthority() throws CdmAuthorityParsingException {
316
        GrantedAuthorityImpl grantedAuthority = GrantedAuthorityImpl.NewInstance(getAuthority());
317
        return grantedAuthority;
318
    }
319
}
(1-1/10)