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 com.nimbusds.oauth2.sdk.ParseException;
022import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
023import net.jcip.annotations.Immutable;
024import net.minidev.json.JSONAware;
025import net.minidev.json.JSONObject;
026
027import java.io.Serializable;
028import java.util.Objects;
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                this.subject = Objects.requireNonNull(subject);
079                this.issuer = issuer;
080                this.parent = parent;
081        }
082
083
084        /**
085         * Returns the subject.
086         *
087         * @return The subject.
088         */
089        public Subject getSubject() {
090                return subject;
091        }
092
093
094        /**
095         * Returns the optional issuer for the subject.
096         *
097         * @return The issuer, {@code null} if not specified.
098         */
099        public Issuer getIssuer() {
100                return issuer;
101        }
102
103
104        /**
105         * Returns the optional parent for this actor.
106         *
107         * @return The optional parent for the actor, {@code null} if none.
108         */
109        public Actor getParent() {
110                return parent;
111        }
112
113
114        /**
115         * Returns a JSON object representation of this actor.
116         *
117         * <p>Simple example:
118         *
119         * <pre>
120         * {
121         *   "sub" : "[email protected]"
122         * }
123         * </pre>
124         *
125         * <p>With nesting:
126         *
127         * <pre>
128         * {
129         *   "sub" : "consumer.example.com-web-application",
130         *   "iss" : "https://issuer.example.net",
131         *   "act" : { "sub":"[email protected]" }
132         * }
133         * </pre>
134         *
135         * @return The JSON object.
136         */
137        public JSONObject toJSONObject() {
138
139                JSONObject o = new JSONObject();
140                o.put("sub", subject.getValue());
141
142                if (issuer != null) {
143                        o.put("iss", issuer.getValue());
144                }
145
146                if (parent != null) {
147                        o.put("act", parent.toJSONObject());
148                }
149
150                return o;
151        }
152
153
154        @Override
155        public int compareTo(final Actor other) {
156
157                return toJSONString().compareTo(other.toJSONString());
158        }
159
160
161        @Override
162        public String toJSONString() {
163                return toJSONObject().toJSONString();
164        }
165        
166        
167        @Override
168        public String toString() {
169                return toJSONString();
170        }
171        
172        
173        @Override
174        public boolean equals(Object o) {
175                if (this == o) return true;
176                if (!(o instanceof Actor)) return false;
177                Actor actor = (Actor) o;
178                return getSubject().equals(actor.getSubject()) && Objects.equals(getIssuer(), actor.getIssuer()) && Objects.equals(getParent(), actor.getParent());
179        }
180        
181        
182        @Override
183        public int hashCode() {
184                return Objects.hash(getSubject(), getIssuer(), getParent());
185        }
186        
187        
188        /**
189         * Parses an actor from the specified JSON object representation.
190         *
191         * <p>Simple example:
192         *
193         * <pre>
194         * {
195         *   "sub" : "[email protected]"
196         * }
197         * </pre>
198         *
199         * <p>With nesting:
200         *
201         * <pre>
202         * {
203         *   "sub" : "consumer.example.com-web-application",
204         *   "iss" : "https://issuer.example.net",
205         *   "act" : { "sub":"[email protected]" }
206         * }
207         * </pre>
208         *
209         * @param jsonObject The JSON object. Must not be {@code null}.
210         *
211         * @return The actor.
212         *
213         * @throws ParseException If parsing failed.
214         */
215        public static Actor parse(final JSONObject jsonObject)
216                throws ParseException {
217
218                Subject sub = new Subject(JSONObjectUtils.getNonBlankString(jsonObject, "sub"));
219
220                Issuer iss = null;
221
222                if (jsonObject.containsKey("iss")) {
223                        iss = new Issuer(JSONObjectUtils.getNonBlankString(jsonObject, "iss"));
224                }
225
226                Actor parent = parseTopLevel(jsonObject);
227
228                return new Actor(sub, iss, parent);
229        }
230
231
232        /**
233         * Parses an actor from the specified top-level JSON object contains
234         * an optional actor JSON representation.
235         *
236         * <p>Simple example:
237         *
238         * <pre>
239         * {
240         *   "aud" : "https://consumer.example.com",
241         *   "iss" : "https://issuer.example.com",
242         *   "exp" : 1443904177,
243         *   "nbf" : 1443904077,
244         *   "sub" : "[email protected]",
245         *   "act" : { "sub" : "[email protected]" }
246         * }
247         * </pre>
248         *
249         * <p>With nesting:
250         *
251         * <pre>
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         * @param jsonObject The top-level JSON object to parse. Must not be
265         *                   {@code null}.
266         *
267         * @return The actor, {@code null} if not specified.
268         *
269         * @throws ParseException If parsing failed.
270         */
271        public static Actor parseTopLevel(final JSONObject jsonObject)
272                throws ParseException {
273
274                JSONObject actSpec = JSONObjectUtils.getJSONObject(jsonObject, "act", null);
275                
276                if (actSpec == null) return null;
277
278                return parse(actSpec);
279        }
280}