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