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 != null && 
118                               object instanceof Value && 
119                               this.toString().equals(object.toString());
120                }
121        }
122
123        
124        /**
125         * Creates a new empty authorisation scope.
126         */
127        public Scope() {
128                // Nothing to do
129        }
130        
131        
132        /**
133         * Returns the string list representation of this scope. The scope
134         * values will be serialised in the order they were added.
135         * 
136         * @return The string list representation.
137         */
138        public List<String> toStringList() {
139                
140                List<String> list = new ArrayList<String>(this.size());
141                
142                for (Scope.Value v: this)
143                        list.add(v.getValue());
144                
145                return list;
146        }
147
148        
149        /**
150         * Returns the string representation of this scope. The scope values 
151         * will be serialised in the order they were added.
152         *
153         * @return The string representation.
154         */
155        @Override
156        public String toString() {
157
158                StringBuilder sb = new StringBuilder();
159
160                for (Scope.Value v : this) {
161
162                        if (sb.length() > 0) {
163                                sb.append(' ');
164                        }
165
166                        sb.append(v.toString());
167                }
168
169                return sb.toString();
170        }
171        
172        
173        /**
174         * Parses a scope from the specified string collection representation.
175         * 
176         * @param collection The string collection, {@code null} if not 
177         *                   specified.
178         * 
179         * @return The scope, {@code null} if not specified.
180         */
181        public static Scope parse(final Collection<String> collection) {
182                
183                if (collection == null)
184                        return null;
185                
186                Scope scope = new Scope();
187                
188                for (String v: collection)
189                        scope.add(new Scope.Value(v));
190                
191                return scope;
192        }
193
194        
195        /**
196         * Parses a scope from the specified string representation.
197         *
198         * @param s The scope string, {@code null} if not specified.
199         *
200         * @return The scope, {@code null} if not specified.
201         */
202        public static Scope parse(final String s) {
203
204                if (s == null)
205                        return null;
206
207                Scope scope = new Scope();
208
209                if (s.trim().isEmpty())
210                        return scope;
211
212                StringTokenizer st = new StringTokenizer(s, " ");
213
214                while(st.hasMoreTokens())
215                        scope.add(new Scope.Value(st.nextToken()));
216
217                return scope;
218        }
219}