001    package com.nimbusds.oauth2.sdk;
002    
003    
004    import java.util.HashSet;
005    
006    import net.jcip.annotations.Immutable;
007    import net.jcip.annotations.NotThreadSafe;
008    
009    import com.nimbusds.oauth2.sdk.id.Identifier;
010    
011    
012    /**
013     * Authorisation scope. This class is not thread-safe.
014     *
015     * <p>Example scope from OpenID Connect indicating access to the user's email
016     * and profile details:
017     *
018     * <pre>
019     * Scope scope = new Scope();
020     * scope.add(OIDCScopeValue.OPENID);
021     * scope.add(OIDCScopeValue.EMAIL);
022     * scope.add(OIDCScopeValue.PROFILE);
023     * </pre>
024     *
025     * <p>Related specifications:
026     *
027     * <ul>
028     * <li>OAuth 2.0 (RFC 6749), section 3.3.
029     * </ul>
030     *
031     * @author Vladimir Dzhuvinov
032     */
033    @NotThreadSafe
034    public class Scope extends HashSet<Scope.Value> {
035    
036            
037            /**
038             * Authorisation scope value. This class is immutable.
039             */
040            @Immutable
041            public static class Value extends Identifier {
042    
043                    /**
044                     * Enumeration of the scope value requirements for 
045                     * application-specific authorisation requests.
046                     */
047                    public static enum Requirement {
048    
049                            /**
050                             * The value must be present in the {@link Scope}
051                             * parameter.
052                             */
053                            REQUIRED,
054                            /**
055                             * The value may be optionally included in the
056                             * {@link Scope} parameter.
057                             */
058                            OPTIONAL
059                    }
060                    
061                    
062                    /**
063                     * Optional requirement.
064                     */
065                    private final Value.Requirement requirement;
066    
067                    /**
068                     * Creates a new scope value. The requirement is not specified.
069                     *
070                     * @param value The scope value. Must not be {@code null} or
071                     *              empty string.
072                     */
073                    public Value(final String value) {
074    
075                            this(value, null);
076                    }
077    
078                    /**
079                     * Creates a new scope value with an optional requirement.
080                     *
081                     * @param value       The scope value. Must not be {@code null} 
082                     *                    or empty string.
083                     * @param requirement The requirement, {@code null} if not
084                     *                    specified.
085                     */
086                    public Value(final String value, final Requirement requirement) {
087    
088                            super(value);
089    
090                            this.requirement = requirement;
091                    }
092    
093                    /**
094                     * Gets the requirement of this scope value.
095                     *
096                     * @return The requirement, {@code null} if not specified.
097                     */
098                    public Requirement getRequirement() {
099    
100                            return requirement;
101                    }
102    
103                    @Override
104                    public boolean equals(final Object object) {
105    
106                            return object != null
107                                    && object instanceof Value
108                                    && this.toString().equals(object.toString());
109                    }
110            }
111    
112            
113            /**
114             * Creates a new empty authorisation scope.
115             */
116            public Scope() {
117                    // Nothing to do
118            }
119    
120            
121            /**
122             * Returns the string representation of this scope. The scope values 
123             * may be serialised in any order.
124             *
125             * @return The string representation.
126             */
127            @Override
128            public String toString() {
129    
130                    StringBuilder sb = new StringBuilder();
131    
132                    for (Scope.Value token : this) {
133    
134                            if (sb.length() > 0) {
135                                    sb.append(' ');
136                            }
137    
138                            sb.append(token.toString());
139                    }
140    
141                    return sb.toString();
142            }
143    
144            
145            /**
146             * Parses a scope from the specified string representation.
147             *
148             * @param s The scope string, {@code null} if not specified.
149             *
150             * @return The scope, {@code null} if not specified.
151             */
152            public static Scope parse(final String s) {
153    
154                    if (s == null) {
155                            return null;
156                    }
157    
158                    Scope scope = new Scope();
159    
160                    if (s.trim().isEmpty()) {
161                            return scope;
162                    }
163    
164                    String[] tokens = s.split("\\s+");
165    
166                    for (String t : tokens) {
167                            scope.add(new Scope.Value(t));
168                    }
169    
170                    return scope;
171            }
172    }