001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk;
019
020
021import java.util.Arrays;
022import java.util.HashSet;
023import java.util.StringTokenizer;
024
025import net.jcip.annotations.Immutable;
026import net.jcip.annotations.NotThreadSafe;
027
028import org.apache.commons.lang3.StringUtils;
029
030import com.nimbusds.oauth2.sdk.id.Identifier;
031
032
033/**
034 * Authorisation response type. Can be single-valued or multiple-valued.
035 *
036 * <p>The following helper methods can be used to find out the OAuth 2.0
037 * protocol flow that a particular response type implies:
038 *
039 * <ul>
040 *     <li>{@link #impliesImplicitFlow}
041 *     <li>{@link #impliesCodeFlow}
042 * </ul>
043 *
044 * <p>Example response type implying an authorisation code flow:
045 *
046 * <pre>
047 * ResponseType() rt = new ResponseType();
048 * rt.add(ResponseType.Value.CODE);
049 * </pre>
050 *
051 * <p>Example response type from OpenID Connect specifying an ID token and an 
052 * access token (implies implicit flow):
053 *
054 * <pre>
055 * ResponseType() rt = new ResponseType();
056 * rt.add(OIDCResponseTypeValue.ID_TOKEN);
057 * rt.add(ResponseType.Value.TOKEN);
058 * </pre>
059 *
060 * <p>Related specifications:
061 *
062 * <ul>
063 *     <li>OAuth 2.0 (RFC 6749), sections 3.1.1 and 4.1.1.
064 *     <li>OAuth 2.0 Multiple Response Type Encoding Practices.
065 * </ul>
066 */
067@NotThreadSafe
068public class ResponseType extends HashSet<ResponseType.Value> {
069        
070        
071        /**
072         * Authorisation response type value.
073         */
074        @Immutable
075        public static final class Value extends Identifier {
076
077                /**
078                 * Authorisation code.
079                 */
080                public static final Value CODE = new Value("code");
081
082                
083                /**
084                 * Access token, with optional refresh token.
085                 */
086                public static final Value TOKEN = new Value("token");
087
088                
089                /**
090                 * Creates a new response type value.
091                 *
092                 * @param value The response type value. Must not be
093                 *              {@code null} or empty string.
094                 */
095                public Value(final String value) {
096
097                        super(value);
098                }
099                
100                
101                @Override
102                public boolean equals(final Object object) {
103
104                        return object instanceof Value &&
105                               this.toString().equals(object.toString());
106                }
107        }
108
109        
110        /**
111         *  Gets the default response type.
112         * 
113         * @return The default response type, consisting of the value
114         *         {@link ResponseType.Value#CODE}.
115         */
116        public static ResponseType getDefault() {
117                
118                ResponseType defaultResponseType = new ResponseType();
119                defaultResponseType.add(ResponseType.Value.CODE);
120                return defaultResponseType;
121        }
122
123        
124        /**
125         * Creates a new empty response type.
126         */
127        public ResponseType() {
128                
129        }
130
131
132        /**
133         * Creates a new response type with the specified string values.
134         *
135         * @param values The string values. Must not be {@code null}.
136         */
137        public ResponseType(final String ... values) {
138
139                for (String v: values)
140                        add(new Value(v));
141        }
142
143
144        /**
145         * Creates a new response type with the specified values.
146         *
147         * @param values The values. Must not be {@code null}.
148         */
149        public ResponseType(final Value ... values) {
150
151                addAll(Arrays.asList(values));
152        }
153        
154        
155        /**
156         * Parses a set of authorisation response types.
157         *
158         * <p>Example serialised response type sets:
159         *
160         * <pre>
161         * code
162         * token
163         * id_token
164         * id_token token
165         * code token
166         * code id_token
167         * code id_token token
168         * </pre>
169         *
170         * @param s Space-delimited list of one or more authorisation response 
171         *          types.
172         *
173         * @return The authorisation response types set.
174         *
175         * @throws ParseException If the parsed string is {@code null} or 
176         *                        empty.
177         */
178        public static ResponseType parse(final String s)
179                throws ParseException {
180        
181                if (StringUtils.isBlank(s))
182                        throw new ParseException("Null or empty response type string");
183        
184                ResponseType rt = new ResponseType();
185                
186                StringTokenizer st = new StringTokenizer(s, " ");
187
188                while (st.hasMoreTokens())
189                        rt.add(new ResponseType.Value(st.nextToken()));
190                
191                return rt;
192        }
193        
194        
195        /**
196         * Returns {@code true} if this response type implies a code flow.
197         *
198         * @return {@code true} if a code flow is implied, else {@code false}.
199         */
200        public boolean impliesCodeFlow() {
201
202                return this.contains(Value.CODE) && this.size() == 1;
203        }
204        
205        
206        /**
207         * Returns {@code true} if this response type implies an implicit flow.
208         *
209         * @return {@code true} if an implicit flow is implied, else 
210         *         {@code false}.
211         */
212        public boolean impliesImplicitFlow() {
213        
214                return ! impliesCodeFlow();
215        }
216
217
218        /**
219         * Checks if this response type contains the specified string value.
220         *
221         * @param value The string value. Must not be {@code null}.
222         *
223         * @return {@code true} if the value is contained, else {@code false}.
224         */
225        public boolean contains(final String value) {
226
227                return contains(new Value(value));
228        }
229        
230        
231        /**
232         * Returns the string representation of this  authorisation response 
233         * type.
234         *
235         * <p>Example serialised response types:
236         *
237         * <pre>
238         * code
239         * token
240         * id_token
241         * id_token token
242         * code token
243         * code id_token
244         * code id_token token
245         * </pre>
246         *
247         * @return Space delimited string representing the authorisation 
248         *         response type.
249         */
250        @Override
251        public String toString() {
252        
253                StringBuilder sb = new StringBuilder();
254
255                for (ResponseType.Value v: this) {
256
257                        if (sb.length() > 0)
258                                sb.append(' ');
259
260                        sb.append(v.getValue());
261                }
262
263                return sb.toString();
264        }
265}