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;
022import java.util.Objects;
023
024import com.nimbusds.oauth2.sdk.ParseException;
025import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
026import net.jcip.annotations.Immutable;
027import net.minidev.json.JSONAware;
028import net.minidev.json.JSONObject;
029
030
031/**
032 * Authorised actor in impersonation and delegation cases.
033 */
034@Immutable
035public final class Actor implements Serializable, Comparable<Actor>, JSONAware {
036        
037        
038        private static final long serialVersionUID = 4171395610729169757L;
039        
040        
041        /**
042         * The actor subject.
043         */
044        private final Subject subject;
045
046
047        /**
048         * Optional issuer for the actor subject.
049         */
050        private final Issuer issuer;
051
052
053        /**
054         * Optional parent for the actor.
055         */
056        private final Actor parent;
057
058
059        /**
060         * Creates a new actor.
061         *
062         * @param subject The subject. Must not be {@code null}.
063         */
064        public Actor(final Subject subject) {
065                this(subject, null, null);
066        }
067
068
069        /**
070         * Creates a new actor.
071         *
072         * @param subject The subject. Must not be {@code null}.
073         * @param issuer  Optional issuer for the subject, {@code null} if
074         *                not specified.
075         * @param parent  Optional parent for the actor, {@code null} if none.
076         */
077        public Actor(final Subject subject, final Issuer issuer, final Actor parent) {
078                if (subject == null) {
079                        throw new IllegalArgumentException("The subject must not be null");
080                }
081                this.subject = subject;
082                this.issuer = issuer;
083                this.parent = parent;
084        }
085
086
087        /**
088         * Returns the subject.
089         *
090         * @return The subject.
091         */
092        public Subject getSubject() {
093                return subject;
094        }
095
096
097        /**
098         * Returns the optional issuer for the subject.
099         *
100         * @return The issuer, {@code null} if not specified.
101         */
102        public Issuer getIssuer() {
103                return issuer;
104        }
105
106
107        /**
108         * Returns the optional parent for this actor.
109         *
110         * @return The optional parent for the actor, {@code null} if none.
111         */
112        public Actor getParent() {
113                return parent;
114        }
115
116
117        /**
118         * Returns a JSON object representation of this actor.
119         *
120         * <p>Simple example:
121         *
122         * <pre>
123         * {
124         *   "sub" : "[email protected]"
125         * }
126         * </pre>
127         *
128         * <p>With nesting:
129         *
130         * <pre>
131         * {
132         *   "sub" : "consumer.example.com-web-application",
133         *   "iss" : "https://issuer.example.net",
134         *   "act" : { "sub":"[email protected]" }
135         * }
136         * </pre>
137         *
138         * @return The JSON object.
139         */
140        public JSONObject toJSONObject() {
141
142                JSONObject o = new JSONObject();
143                o.put("sub", subject.getValue());
144
145                if (issuer != null) {
146                        o.put("iss", issuer.getValue());
147                }
148
149                if (parent != null) {
150                        o.put("act", parent.toJSONObject());
151                }
152
153                return o;
154        }
155
156
157        @Override
158        public int compareTo(final Actor other) {
159
160                return toJSONString().compareTo(other.toJSONString());
161        }
162
163
164        @Override
165        public String toJSONString() {
166                return toJSONObject().toJSONString();
167        }
168        
169        
170        @Override
171        public String toString() {
172                return toJSONString();
173        }
174        
175        
176        @Override
177        public boolean equals(Object o) {
178                if (this == o) return true;
179                if (!(o instanceof Actor)) return false;
180                Actor actor = (Actor) o;
181                return getSubject().equals(actor.getSubject()) && Objects.equals(getIssuer(), actor.getIssuer()) && Objects.equals(getParent(), actor.getParent());
182        }
183        
184        
185        @Override
186        public int hashCode() {
187                return Objects.hash(getSubject(), getIssuer(), getParent());
188        }
189        
190        
191        /**
192         * Parses an actor from the specified JSON object representation.
193         *
194         * <p>Simple example:
195         *
196         * <pre>
197         * {
198         *   "sub" : "[email protected]"
199         * }
200         * </pre>
201         *
202         * <p>With nesting:
203         *
204         * <pre>
205         * {
206         *   "sub" : "consumer.example.com-web-application",
207         *   "iss" : "https://issuer.example.net",
208         *   "act" : { "sub":"[email protected]" }
209         * }
210         * </pre>
211         *
212         * @param jsonObject The JSON object. Must not be {@code null}.
213         *
214         * @return The actor.
215         *
216         * @throws ParseException If parsing failed.
217         */
218        public static Actor parse(final JSONObject jsonObject)
219                throws ParseException {
220
221                Subject sub = new Subject(JSONObjectUtils.getString(jsonObject, "sub"));
222
223                Issuer iss = null;
224
225                if (jsonObject.containsKey("iss")) {
226                        iss = new Issuer(JSONObjectUtils.getString(jsonObject, "iss"));
227                }
228
229                Actor parent = parseTopLevel(jsonObject);
230
231                return new Actor(sub, iss, parent);
232        }
233
234
235        /**
236         * Parses an actor from the specified top-level JSON object contains
237         * an optional actor JSON representation.
238         *
239         * <p>Simple example:
240         *
241         * <pre>
242         * {
243         *   "aud" : "https://consumer.example.com",
244         *   "iss" : "https://issuer.example.com",
245         *   "exp" : 1443904177,
246         *   "nbf" : 1443904077,
247         *   "sub" : "[email protected]",
248         *   "act" : { "sub" : "[email protected]" }
249         * }
250         * </pre>
251         *
252         * <p>With nesting:
253         *
254         * <pre>
255         * {
256         *   "aud" : "https://backend.example.com",
257         *   "iss" : "https://issuer.example.com",
258         *   "exp" : 1443904100,
259         *   "nbf" : 1443904000,
260         *   "sub" : "[email protected]",
261         *   "act" : { "sub" : "consumer.example.com-web-application",
262         *             "iss" : "https://issuer.example.net",
263         *             "act" : { "sub":"[email protected]" } }
264         * }
265         * </pre>
266         *
267         * @param jsonObject The top-level JSON object to parse. Must not be
268         *                   {@code null}.
269         *
270         * @return The actor, {@code null} if not specified.
271         *
272         * @throws ParseException If parsing failed.
273         */
274        public static Actor parseTopLevel(final JSONObject jsonObject)
275                throws ParseException {
276
277                JSONObject actSpec = JSONObjectUtils.getJSONObject(jsonObject, "act", null);
278                
279                if (actSpec == null) return null;
280
281                return parse(actSpec);
282        }
283}