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