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