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 String toString() { 168 return toJSONString(); 169 } 170 171 172 @Override 173 public boolean equals(Object o) { 174 if (this == o) return true; 175 if (!(o instanceof Actor)) return false; 176 177 Actor actor = (Actor) o; 178 179 if (!subject.equals(actor.subject)) return false; 180 if (issuer != null ? !issuer.equals(actor.issuer) : actor.issuer != null) 181 return false; 182 return parent != null ? parent.equals(actor.parent) : actor.parent == null; 183 184 } 185 186 187 @Override 188 public int hashCode() { 189 int result = subject.hashCode(); 190 result = 31 * result + (issuer != null ? issuer.hashCode() : 0); 191 result = 31 * result + (parent != null ? parent.hashCode() : 0); 192 return result; 193 } 194 195 196 /** 197 * Parses an actor from the specified JSON object representation. 198 * 199 * <p>Simple example: 200 * 201 * <pre> 202 * { 203 * "sub" : "[email protected]" 204 * } 205 * </pre> 206 * 207 * <p>With nesting: 208 * 209 * <pre> 210 * { 211 * "sub" : "consumer.example.com-web-application", 212 * "iss" : "https://issuer.example.net", 213 * "act" : { "sub":"[email protected]" } 214 * } 215 * </pre> 216 * 217 * @param jsonObject The JSON object. Must not be {@code null}. 218 * 219 * @return The actor. 220 * 221 * @throws ParseException If parsing failed. 222 */ 223 public static Actor parse(final JSONObject jsonObject) 224 throws ParseException { 225 226 Subject sub = new Subject(JSONObjectUtils.getString(jsonObject, "sub")); 227 228 Issuer iss = null; 229 230 if (jsonObject.containsKey("iss")) { 231 iss = new Issuer(JSONObjectUtils.getString(jsonObject, "iss")); 232 } 233 234 Actor parent = parseTopLevel(jsonObject); 235 236 return new Actor(sub, iss, parent); 237 } 238 239 240 /** 241 * Parses an actor from the specified top-level JSON object contains 242 * an optional actor JSON representation. 243 * 244 * <p>Simple example: 245 * 246 * <pre> 247 * { 248 * "aud" : "https://consumer.example.com", 249 * "iss" : "https://issuer.example.com", 250 * "exp" : 1443904177, 251 * "nbf" : 1443904077, 252 * "sub" : "[email protected]", 253 * "act" : { "sub" : "[email protected]" } 254 * } 255 * </pre> 256 * 257 * <p>With nesting: 258 * 259 * <pre> 260 * { 261 * "aud" : "https://backend.example.com", 262 * "iss" : "https://issuer.example.com", 263 * "exp" : 1443904100, 264 * "nbf" : 1443904000, 265 * "sub" : "[email protected]", 266 * "act" : { "sub" : "consumer.example.com-web-application", 267 * "iss" : "https://issuer.example.net", 268 * "act" : { "sub":"[email protected]" } } 269 * } 270 * </pre> 271 * 272 * @param jsonObject The top-level JSON object to parse. Must not be 273 * {@code null}. 274 * 275 * @return The actor, {@code null} if not specified. 276 * 277 * @throws ParseException If parsing failed. 278 */ 279 public static Actor parseTopLevel(final JSONObject jsonObject) 280 throws ParseException { 281 282 JSONObject actSpec = JSONObjectUtils.getJSONObject(jsonObject, "act", null); 283 284 if (actSpec == null) return null; 285 286 return parse(actSpec); 287 } 288}