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