001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk.id;
019
020
021import java.io.Serializable;
022
023import com.nimbusds.oauth2.sdk.ParseException;
024import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
025import net.jcip.annotations.Immutable;
026import net.minidev.json.JSONAware;
027import net.minidev.json.JSONObject;
028
029
030/**
031 * Authorised actor in impersonation and delegation cases.
032 */
033@Immutable
034public final class Actor implements Serializable, Comparable<Actor>, JSONAware {
035        
036
037        /**
038         * The actor subject.
039         */
040        private final Subject subject;
041
042
043        /**
044         * Optional issuer for the actor subject.
045         */
046        private final Issuer issuer;
047
048
049        /**
050         * Optional parent for the actor.
051         */
052        private final Actor parent;
053
054
055        /**
056         * Creates a new actor.
057         *
058         * @param subject The subject. Must not be {@code null}.
059         */
060        public Actor(final Subject subject) {
061                this(subject, null, null);
062        }
063
064
065        /**
066         * Creates a new actor.
067         *
068         * @param subject The subject. Must not be {@code null}.
069         * @param issuer  Optional issuer for the subject, {@code null} if
070         *                not specified.
071         * @param parent  Optional parent for the actor, {@code null} if none.
072         */
073        public Actor(final Subject subject, final Issuer issuer, final Actor parent) {
074                if (subject == null) {
075                        throw new IllegalArgumentException("The subject must not be null");
076                }
077                this.subject = subject;
078                this.issuer = issuer;
079                this.parent = parent;
080        }
081
082
083        /**
084         * Returns the subject.
085         *
086         * @return The subject.
087         */
088        public Subject getSubject() {
089                return subject;
090        }
091
092
093        /**
094         * Returns the optional issuer for the subject.
095         *
096         * @return The issuer, {@code null} if not specified.
097         */
098        public Issuer getIssuer() {
099                return issuer;
100        }
101
102
103        /**
104         * Returns the optional parent for this actor.
105         *
106         * @return The optional parent for the actor, {@code null} if none.
107         */
108        public Actor getParent() {
109                return parent;
110        }
111
112
113        /**
114         * Returns a JSON object representation of this actor.
115         *
116         * <p>Simple example:
117         *
118         * <pre>
119         * {
120         *   "sub" : "[email protected]"
121         * }
122         * </pre>
123         *
124         * <p>With nesting:
125         *
126         * <pre>
127         * {
128         *   "sub" : "consumer.example.com-web-application",
129         *   "iss" : "https://issuer.example.net",
130         *   "act" : { "sub":"[email protected]" }
131         * }
132         * </pre>
133         *
134         * @return The JSON object.
135         */
136        public JSONObject toJSONObject() {
137
138                JSONObject o = new JSONObject();
139                o.put("sub", subject.getValue());
140
141                if (issuer != null) {
142                        o.put("iss", issuer.getValue());
143                }
144
145                if (parent != null) {
146                        o.put("act", parent.toJSONObject());
147                }
148
149                return o;
150        }
151
152
153        @Override
154        public int compareTo(final Actor other) {
155
156                return toJSONString().compareTo(other.toJSONString());
157        }
158
159
160        @Override
161        public String toJSONString() {
162                return toJSONObject().toJSONString();
163        }
164
165
166        @Override
167        public boolean equals(Object o) {
168                if (this == o) return true;
169                if (!(o instanceof Actor)) return false;
170
171                Actor actor = (Actor) o;
172
173                if (!subject.equals(actor.subject)) return false;
174                if (issuer != null ? !issuer.equals(actor.issuer) : actor.issuer != null)
175                        return false;
176                return parent != null ? parent.equals(actor.parent) : actor.parent == null;
177
178        }
179
180
181        @Override
182        public int hashCode() {
183                int result = subject.hashCode();
184                result = 31 * result + (issuer != null ? issuer.hashCode() : 0);
185                result = 31 * result + (parent != null ? parent.hashCode() : 0);
186                return result;
187        }
188
189
190        /**
191         * Parses an actor from the specified JSON object representation.
192         *
193         * <p>Simple example:
194         *
195         * <pre>
196         * {
197         *   "sub" : "[email protected]"
198         * }
199         * </pre>
200         *
201         * <p>With nesting:
202         *
203         * <pre>
204         * {
205         *   "sub" : "consumer.example.com-web-application",
206         *   "iss" : "https://issuer.example.net",
207         *   "act" : { "sub":"[email protected]" }
208         * }
209         * </pre>
210         *
211         * @param jsonObject The JSON object. Must not be {@code null}.
212         *
213         * @return The actor.
214         *
215         * @throws ParseException If parsing failed.
216         */
217        public static Actor parse(final JSONObject jsonObject)
218                throws ParseException {
219
220                Subject sub = new Subject(JSONObjectUtils.getString(jsonObject, "sub"));
221
222                Issuer iss = null;
223
224                if (jsonObject.containsKey("iss")) {
225                        iss = new Issuer(JSONObjectUtils.getString(jsonObject, "iss"));
226                }
227
228                Actor parent = parseTopLevel(jsonObject);
229
230                return new Actor(sub, iss, parent);
231        }
232
233
234        /**
235         * Parses an actor from the specified top-level JSON object contains
236         * an optional actor JSON representation.
237         *
238         * <p>Simple example:
239         *
240         * <pre>
241         * {
242         *   "aud" : "https://consumer.example.com",
243         *   "iss" : "https://issuer.example.com",
244         *   "exp" : 1443904177,
245         *   "nbf" : 1443904077,
246         *   "sub" : "[email protected]",
247         *   "act" : { "sub" : "[email protected]" }
248         * }
249         * </pre>
250         *
251         * <p>With nesting:
252         * {
253         *   "aud" : "https://backend.example.com",
254         *   "iss" : "https://issuer.example.com",
255         *   "exp" : 1443904100,
256         *   "nbf" : 1443904000,
257         *   "sub" : "[email protected]",
258         *   "act" : { "sub" : "consumer.example.com-web-application",
259         *             "iss" : "https://issuer.example.net",
260         *             "act" : { "sub":"[email protected]" } }
261         * }
262         * <pre>
263         *
264         * </pre>
265         *
266         * @param jsonObject The top-level JSON object to parse. Must not be
267         *                   {@code null}.
268         *
269         * @return The actor, {@code null} if not specified.
270         *
271         * @throws ParseException If parsing failed.
272         */
273        public static Actor parseTopLevel(final JSONObject jsonObject)
274                throws ParseException {
275
276                if (! jsonObject.containsKey("act")) {
277                        return null;
278                }
279
280                return parse(JSONObjectUtils.getJSONObject(jsonObject, "act"));
281        }
282}