Project

General

Profile

Download (11.1 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.hibernate.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.common.GrantedAuthorityImpl;
26

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

    
68
    private static final long serialVersionUID = 1L;
69

    
70
    public static final Logger logger = Logger.getLogger(CdmAuthority.class);
71

    
72
    private static Map<String, CdmAuthority> grantedAuthorityCache = new HashMap<>();
73

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

    
82
    public CdmAuthority(CdmBase targetDomainObject, EnumSet<CRUD> operation){
83

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

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

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

    
113

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

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

    
130
    protected CdmAuthority (String authority) throws CdmAuthorityParsingException{
131

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

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

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

    
155
    public CdmPermissionClass getPermissionClass(){
156
        return permissionClass;
157
    }
158

    
159
    public String getProperty(){
160
        return property;
161
    }
162

    
163
    public EnumSet<CRUD> getOperation(){
164
        return operation;
165
    }
166

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

    
171
    public UUID getTargetUUID(){
172
        return targetUuid;
173
    }
174

    
175
    public boolean hasTargetUuid() {
176
        return targetUuid != null;
177
    }
178

    
179
    public boolean hasProperty() {
180
        return property != null;
181
    }
182

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

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

    
225
        return tokens;
226
    }
227

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

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

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

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

    
270

    
271

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

    
285
    /**
286
     * @return
287
     */
288
    protected String operationsToString() {
289
        String[] opsstr = new String[operation.size()];
290
        int i = 0;
291
        for(CRUD crud : operation){
292
            opsstr[i++] = crud.name();
293
        }
294
        String asString = StringUtils.join(opsstr, ",");
295
        return "[" + asString + "]";
296
    }
297

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

    
319

    
320
    @Override
321
    public GrantedAuthorityImpl asNewGrantedAuthority() throws CdmAuthorityParsingException {
322
        GrantedAuthorityImpl grantedAuthority = GrantedAuthorityImpl.NewInstance(getAuthority());
323
        return grantedAuthority;
324
    }
325

    
326

    
327
}
(2-2/10)