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}