001package com.nimbusds.openid.connect.provider.spi.grants;
002
003
004import java.util.ArrayList;
005import java.util.List;
006
007import net.jcip.annotations.Immutable;
008import net.minidev.json.JSONObject;
009import org.checkerframework.checker.nullness.qual.Nullable;
010
011import com.nimbusds.oauth2.sdk.ParseException;
012import com.nimbusds.oauth2.sdk.id.Audience;
013import com.nimbusds.oauth2.sdk.id.Subject;
014import com.nimbusds.oauth2.sdk.util.CollectionUtils;
015import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
016
017
018/**
019 * Base token specification.
020 */
021@Immutable
022public class TokenSpec {
023
024
025        /**
026         * The token lifetime, in seconds. For access tokens zero and negative
027         * implies not specified (to let the Connect2id server apply the
028         * default configured access token lifetime). For refresh tokens zero
029         * implies permanent (no expiration) and negative not specified (to let
030         * the Connect2id server apply the default configured refresh token
031         * lifetime).
032         */
033        private final long lifetime;
034
035
036        /**
037         * Explicit list of audiences for the token, {@code null} if none.
038         */
039        private final @Nullable List<Audience> audList;
040
041
042        /**
043         * The subject in impersonation and delegation cases, {@code null} if
044         * not applicable.
045         */
046        private final @Nullable Subject impersonatedSubject;
047
048
049        /**
050         * Creates a new token specification. No explicit token audience is
051         * specified. No subject in impersonation and delegation cases is
052         * specified.
053         *
054         * @param lifetime The token lifetime, in seconds. For access tokens
055         *                 zero and negative implies not specified (to let the
056         *                 Connect2id server apply the default configured
057         *                 access token lifetime). For refresh tokens zero
058         *                 implies permanent (no expiration) and negative not
059         *                 specified (to let the Connect2id server apply the
060         *                 default configured refresh token lifetime).
061         */
062        public TokenSpec(final long lifetime) {
063
064                this(lifetime, null, null);
065        }
066
067
068        /**
069         * Creates a new token specification.
070         *
071         * @param lifetime            The token lifetime, in seconds. For
072         *                            access tokens zero and negative implies
073         *                            not specified (to let the Connect2id
074         *                            server apply the default configured
075         *                            access token lifetime). For refresh
076         *                            tokens zero implies permanent (no
077         *                            expiration) and negative not specified
078         *                            (to let the Connect2id server apply the
079         *                            default configured refresh token
080         *                            lifetime).
081         * @param audList             Explicit list of audiences for the token,
082         *                            {@code null} if not specified.
083         * @param impersonatedSubject The subject in impersonation and
084         *                            delegation cases, {@code null} if not
085         *                            applicable.
086         */
087        public TokenSpec(final long lifetime, final @Nullable List<Audience> audList, final @Nullable Subject impersonatedSubject) {
088
089                this.lifetime = lifetime;
090                this.audList = audList;
091                this.impersonatedSubject = impersonatedSubject;
092        }
093
094
095        /**
096         * Returns the token lifetime.
097         *
098         * @return The token lifetime, in seconds. For access tokens zero and
099         *         negative implies not specified (to let the Connect2id server
100         *         apply the default configured access token lifetime). For
101         *         refresh tokens zero implies permanent (no expiration) and
102         *         negative not specified (to let the Connect2id server apply
103         *         the default configured refresh token lifetime).
104         */
105        public long getLifetime() {
106
107                return lifetime;
108        }
109
110
111        /**
112         * Returns the explicit list of audiences for the token.
113         *
114         * @return The explicit list of audiences for the token, {@code null}
115         *         if not specified.
116         */
117        public @Nullable List<Audience> getAudience() {
118
119                return audList;
120        }
121
122
123        /**
124         * Returns the subject in impersonation and delegation cases.
125         *
126         * @return The subject in impersonation and delegation cases,
127         *         {@code null} if not applicable.
128         */
129        public @Nullable Subject getImpersonatedSubject() {
130
131                return impersonatedSubject;
132        }
133
134
135        /**
136         * Returns a JSON object representation of this token specification.
137         *
138         * @return The JSON object.
139         */
140        public JSONObject toJSONObject() {
141
142                JSONObject o = new JSONObject();
143
144                if (lifetime >= 0L) {
145                        o.put("lifetime", lifetime);
146                }
147
148                if (CollectionUtils.isNotEmpty(audList)) {
149
150                        List<String> sl = new ArrayList<>(audList.size());
151
152                        for (Audience aud: audList) {
153                                sl.add(aud.getValue());
154                        }
155
156                        o.put("audience", sl);
157                }
158
159                if (impersonatedSubject != null) {
160                        o.put("impersonated_sub", impersonatedSubject.getValue());
161                }
162
163                return o;
164        }
165
166
167        @Override
168        public String toString() {
169
170                return toJSONObject().toJSONString();
171        }
172
173
174        /**
175         * Parses a token specification from the specified JSON object.
176         *
177         * @param jsonObject The JSON object. Must not be {@code null}.
178         *
179         * @return The token specification.
180         *
181         * @throws ParseException If parsing failed.
182         */
183        public static TokenSpec parse(final JSONObject jsonObject)
184                throws ParseException {
185
186                long lifetime = -1L;
187
188                if (jsonObject.containsKey("lifetime")) {
189                        lifetime = JSONObjectUtils.getLong(jsonObject, "lifetime");
190                }
191
192                List<Audience> audList = null;
193                
194                if (jsonObject.containsKey("audience")) {
195                        audList = Audience.create(JSONObjectUtils.getStringList(jsonObject, "audience"));
196                }
197
198                Subject impersonatedSub = null;
199
200                if (jsonObject.containsKey("impersonated_sub")) {
201                        try {
202                                impersonatedSub = new Subject(JSONObjectUtils.getString(jsonObject, "impersonated_sub"));
203                        } catch (IllegalArgumentException e) {
204                                // On invalid subject
205                                throw new ParseException(e.getMessage(), e);
206                        }
207                }
208
209                return new TokenSpec(lifetime, audList, impersonatedSub);
210        }
211}