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        @Deprecated
107        public GrantAuthorization(final Scope scope,
108                                  final List<Audience> audList,
109                                  final AccessTokenSpec accessTokenSpec,
110                                  final JSONObject data) {
111
112                this(scope,
113                        new AccessTokenSpec(
114                                accessTokenSpec.getLifetime(),
115                                audList, // override with top-level parameter, backward compat API
116                                accessTokenSpec.getEncoding(),
117                                accessTokenSpec.getImpersonatedSubject(),
118                                accessTokenSpec.encrypt(),
119                                accessTokenSpec.getSubjectType()),
120                        data);
121        }
122
123
124        /**
125         * Returns the authorised scope values.
126         *
127         * @return The authorised scope values.
128         */
129        public Scope getScope() {
130
131                return scope;
132        }
133
134
135        /**
136         * Returns the explicit list of audiences for the access token.
137         *
138         * @return The explicit list of audiences for the access token,
139         *         {@code null} if not specified.
140         */
141        @Deprecated
142        public List<Audience> getAudience() {
143
144                return getAccessTokenSpec().getAudience();
145        }
146
147
148        /**
149         * Returns the access token specification.
150         *
151         * @return The access token specification.
152         */
153        public AccessTokenSpec getAccessTokenSpec() {
154
155                return accessTokenSpec;
156        }
157
158
159        /**
160         * Returns the additional data as a JSON object.
161         *
162         * @return The additional data, {@code null} if not specified.
163         */
164        public JSONObject getData() {
165
166                return data;
167        }
168
169
170        /**
171         * Returns a JSON object representation of this authorisation.
172         *
173         * @return The JSON object representation.
174         */
175        public JSONObject toJSONObject() {
176
177                JSONObject o = new JSONObject();
178
179                o.put("scope", scope.toStringList());
180
181                JSONObject accessTokenSpecJSONObject = accessTokenSpec.toJSONObject();
182
183                // Backward API compat
184                // TODO remove in future major server version, deprecated in server v12.0
185                if (CollectionUtils.isNotEmpty(getAccessTokenSpec().getAudience())) {
186                        o.put("audience", accessTokenSpecJSONObject.get("audience"));
187                }
188
189                o.put("access_token", accessTokenSpecJSONObject);
190
191                if (MapUtils.isNotEmpty(data)) {
192                        o.put("data", data);
193                }
194
195                return o;
196        }
197
198
199        /**
200         * Parses a basic authorisation from the specified JSON object.
201         *
202         * @param jsonObject The JSON object to parse. Must not be
203         *                   {@code null}.
204         *
205         * @return The basic authorisation.
206         *
207         * @throws ParseException If parsing failed.
208         */
209        public static GrantAuthorization parse(final JSONObject jsonObject)
210                throws ParseException {
211
212                Scope scope = Scope.parse(Arrays.asList(JSONObjectUtils.getStringArray(jsonObject, "scope")));
213
214                // Backward API compat
215                List<Audience> topLevelAudList = null;
216                if (jsonObject.containsKey("audience")) {
217                        String[] sa = JSONObjectUtils.getStringArray(jsonObject, "audience");
218                        topLevelAudList = new ArrayList<>(sa.length);
219                        for (String s: sa) {
220                                topLevelAudList.add(new Audience(s));
221                        }
222                }
223
224                AccessTokenSpec accessTokenSpec;
225
226                if (jsonObject.get("access_token") != null) {
227                        // Parse
228                        JSONObject accessTokenJSONObject = JSONObjectUtils.getJSONObject(jsonObject, "access_token");
229                        if (topLevelAudList != null) {
230                                accessTokenJSONObject.put("audience", Audience.toStringList(topLevelAudList));
231                        }
232                        accessTokenSpec = AccessTokenSpec.parse(accessTokenJSONObject);
233                        if (topLevelAudList != null) {
234                                accessTokenSpec = new AccessTokenSpec(
235                                        accessTokenSpec.getLifetime(),
236                                        topLevelAudList, // Backward API compat
237                                        accessTokenSpec.getEncoding(),
238                                        accessTokenSpec.getImpersonatedSubject(),
239                                        accessTokenSpec.encrypt(),
240                                        accessTokenSpec.getSubjectType());
241                        }
242                } else {
243                        // Apply default settings
244                        accessTokenSpec = new AccessTokenSpec();
245                }
246
247                JSONObject data = null;
248
249                if (jsonObject.containsKey("data")) {
250                        data = JSONObjectUtils.getJSONObject(jsonObject, "data");
251                }
252
253                return new GrantAuthorization(scope, accessTokenSpec, data);
254        }
255
256
257        /**
258         * Parses a basic authorisation from the specified JSON object string.
259         *
260         * @param json The JSON object string to parse. Must not be
261         *             {@code null}.
262         *
263         * @return The basic authorisation.
264         *
265         * @throws ParseException If parsing failed.
266         */
267        public static GrantAuthorization parse(final String json)
268                throws ParseException {
269
270                return parse(JSONObjectUtils.parse(json));
271        }
272}