001package com.nimbusds.openid.connect.provider.spi.grants;
002
003
004import java.util.ArrayList;
005import java.util.Arrays;
006import java.util.List;
007
008import net.jcip.annotations.Immutable;
009
010import net.minidev.json.JSONObject;
011
012import org.apache.commons.collections4.CollectionUtils;
013import org.apache.commons.collections4.MapUtils;
014
015import com.nimbusds.oauth2.sdk.ParseException;
016import com.nimbusds.oauth2.sdk.Scope;
017import com.nimbusds.oauth2.sdk.id.Audience;
018import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
019
020
021/**
022 * Basic OAuth 2.0 authorisation produced by a {@link GrantHandler}.
023 *
024 * <p>Required authorisation details:
025 *
026 * <ul>
027 *     <li>The authorised scope.
028 * </ul>
029 *
030 * <p>All other parameters are optional or have suitable defaults.
031 */
032@Immutable
033public class GrantAuthorization {
034
035
036        /**
037         * The authorised scope values.
038         */
039        private final Scope scope;
040
041
042        /**
043         * The access token specification.
044         */
045        private final AccessTokenSpec accessTokenSpec;
046
047
048        /**
049         * Optional authorisation data as a JSON object, {@code null} if not
050         * specified.
051         */
052        private final JSONObject data;
053
054
055        /**
056         * Creates a new basic authorisation.
057         *
058         * @param scope The authorised scope values. Must not be {@code null}.
059         */
060        public GrantAuthorization(final Scope scope) {
061
062                this(scope, AccessTokenSpec.DEFAULT, null);
063        }
064
065
066        /**
067         * Creates a new basic authorisation.
068         *
069         * @param scope           The authorised scope values. Must not be
070         *                        {@code null}.
071         * @param accessTokenSpec The access token specification. Must not be
072         *                        {@code null}.
073         * @param data            Additional data as a JSON object,
074         *                        {@code null} if not specified.
075         */
076        public GrantAuthorization(final Scope scope,
077                                  final AccessTokenSpec accessTokenSpec,
078                                  final JSONObject data) {
079
080                if (scope == null) {
081                        throw new IllegalArgumentException("The scope must not be null");
082                }
083
084                this.scope = scope;
085
086                if (accessTokenSpec == null) {
087                        throw new IllegalArgumentException("The access token specification must not be null");
088                }
089
090                this.accessTokenSpec = accessTokenSpec;
091
092                this.data = data;
093        }
094
095
096        /**
097         * Creates a new basic authorisation.
098         *
099         * @param scope           The authorised scope values. Must not be
100         *                        {@code null}.
101         * @param audList         Explicit list of audiences for the access
102         *                        token, {@code null} if not specified.
103         * @param accessTokenSpec The access token specification. Must not be
104         *                        {@code null}.
105         * @param data            Additional data as a JSON object,
106         *                        {@code null} if not specified.
107         */
108        public GrantAuthorization(final Scope scope,
109                                  final List<Audience> audList,
110                                  final AccessTokenSpec accessTokenSpec,
111                                  final JSONObject data) {
112
113                this(scope,
114                        new AccessTokenSpec(
115                                accessTokenSpec.getLifetime(),
116                                audList, // override with top-level parameter, backward compat API
117                                accessTokenSpec.getEncoding(),
118                                accessTokenSpec.encrypt()),
119                        data);
120        }
121
122
123        /**
124         * Returns the authorised scope values.
125         *
126         * @return The authorised scope values.
127         */
128        public Scope getScope() {
129
130                return scope;
131        }
132
133
134        /**
135         * Returns the explicit list of audiences for the access token.
136         *
137         * @return The explicit list of audiences for the access token,
138         *         {@code null} if not specified.
139         */
140        public List<Audience> getAudience() {
141
142                return getAccessTokenSpec().getAudience();
143        }
144
145
146        /**
147         * Returns the access token specification.
148         *
149         * @return The access token specification.
150         */
151        public AccessTokenSpec getAccessTokenSpec() {
152
153                return accessTokenSpec;
154        }
155
156
157        /**
158         * Returns the additional data as a JSON object.
159         *
160         * @return The additional data, {@code null} if not specified.
161         */
162        public JSONObject getData() {
163
164                return data;
165        }
166
167
168        /**
169         * Returns a JSON object representation of this authorisation.
170         *
171         * @return The JSON object representation.
172         */
173        public JSONObject toJSONObject() {
174
175                JSONObject o = new JSONObject();
176
177                o.put("scope", scope.toStringList());
178
179                JSONObject accessTokenSpecJSONObject = accessTokenSpec.toJSONObject();
180
181                // Backward API compat
182                if (CollectionUtils.isNotEmpty(getAccessTokenSpec().getAudience())) {
183                        o.put("audience", accessTokenSpecJSONObject.remove("audience"));
184                }
185
186                o.put("access_token", accessTokenSpecJSONObject);
187
188                if (MapUtils.isNotEmpty(data)) {
189                        o.put("data", data);
190                }
191
192                return o;
193        }
194
195
196        /**
197         * Parses a basic authorisation from the specified JSON object.
198         *
199         * @param jsonObject The JSON object to parse. Must not be
200         *                   {@code null}.
201         *
202         * @return The basic authorisation.
203         *
204         * @throws ParseException If parsing failed.
205         */
206        public static GrantAuthorization parse(final JSONObject jsonObject)
207                throws ParseException {
208
209                Scope scope = Scope.parse(Arrays.asList(JSONObjectUtils.getStringArray(jsonObject, "scope")));
210
211                // Backward API compat
212                List<Audience> topLevelAudList = null;
213
214                if (jsonObject.containsKey("audience")) {
215                        String[] sa = JSONObjectUtils.getStringArray(jsonObject, "audience");
216                        topLevelAudList = new ArrayList<>(sa.length);
217                        for (String s: sa) {
218                                topLevelAudList.add(new Audience(s));
219                        }
220                }
221
222                AccessTokenSpec accessTokenSpec;
223
224                if (jsonObject.containsKey("access_token")) {
225                        // Parse
226                        accessTokenSpec = AccessTokenSpec.parse(JSONObjectUtils.getJSONObject(jsonObject, "access_token"));
227                        if (topLevelAudList != null) {
228                                accessTokenSpec = new AccessTokenSpec(
229                                        accessTokenSpec.getLifetime(),
230                                        topLevelAudList, // Backward API compat
231                                        accessTokenSpec.getEncoding(),
232                                        accessTokenSpec.encrypt());
233                        }
234                } else {
235                        // Apply default settings
236                        accessTokenSpec = new AccessTokenSpec();
237                }
238
239                JSONObject data = null;
240
241                if (jsonObject.containsKey("data")) {
242                        data = JSONObjectUtils.getJSONObject(jsonObject, "data");
243                }
244
245                return new GrantAuthorization(scope, accessTokenSpec, data);
246        }
247
248
249        /**
250         * Parses a basic authorisation from the specified JSON object string.
251         *
252         * @param json The JSON object string to parse. Must not be
253         *             {@code null}.
254         *
255         * @return The basic authorisation.
256         *
257         * @throws ParseException If parsing failed.
258         */
259        public static GrantAuthorization parse(final String json)
260                throws ParseException {
261
262                return parse(JSONObjectUtils.parse(json));
263        }
264}