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