001package com.nimbusds.common.id; 002 003 004import java.text.ParseException; 005 006import com.unboundid.ldap.sdk.DN; 007import com.unboundid.ldap.sdk.LDAPException; 008 009 010/** 011 * Represents a SASL authorisation identity, as specified in RFC 4513, section 012 * 5.2.1.8. This can be a distinguished name (DN) or a username. 013 * 014 * <p>Note that for the purpose of keying and comparing authorisation 015 * identities, the DNs are normalised and the usernames are converted to lower 016 * case. 017 * 018 * <p>DN form: 019 * 020 * <pre> 021 * "dn: uid=alice,ou=people,dc=wonderland,dc=net" 022 * </pre> 023 * 024 * <p>Username form: 025 * 026 * <pre> 027 * "u: alice" 028 * </pre> 029 */ 030public final class AuthzId extends BaseIdentifier { 031 032 033 /** 034 * The identity data types. 035 */ 036 public enum IdentityType { 037 038 /** 039 * Distinguished name (DN). 040 */ 041 DN, 042 043 044 /** 045 * Username. 046 */ 047 USERNAME 048 } 049 050 051 /** 052 * Anonymous user identity. 053 */ 054 public static final AuthzId ANONYMOUS = new AuthzId(DN.NULL_DN); 055 056 057 /** 058 * The authorisation identity as DN, {@code null} if specified by a 059 * username. 060 */ 061 private final DN dn; 062 063 064 /** 065 * The authorisation identity as username, {@code null} if specified by 066 * a DN. 067 */ 068 private final Username username; 069 070 071 072 /** 073 * The identity type. 074 */ 075 private final IdentityType identityType; 076 077 078 /** 079 * Parses a string representation of a SASL authorisation identity. 080 * 081 * @param value The string to parse, if {@code null} or empty 082 * {@link #ANONYMOUS} is returned. 083 * 084 * @return The parsed authorisation identity. 085 * 086 * @throws ParseException On a bad authzid syntax. 087 */ 088 public static AuthzId parse(final String value) 089 throws ParseException { 090 091 if (value == null || value.trim().isEmpty()) 092 return ANONYMOUS; 093 094 if (value.toLowerCase().startsWith("dn:")) { 095 096 DN dn; 097 098 try { 099 // there may be whitespace between "dn:" and the DN value 100 dn = new DN(value.substring(3).trim()); 101 102 } catch (LDAPException e) { 103 104 throw new ParseException("Bad authzId syntax: " + e.getMessage(), 3); 105 } 106 107 return new AuthzId(dn); 108 109 } else if (value.toLowerCase().startsWith("u:")) { 110 111 // there may be whitespace between "u:" and the username value 112 return new AuthzId(new Username(value.substring(2))); 113 114 } else { 115 throw new ParseException("Bad authzId syntax: String must start with dn: or u:", 0); 116 } 117 } 118 119 120 /** 121 * Returns the authzId string representation of the specified 122 * distinguished name (DN). 123 * 124 * @param dn The distinguished name (DN). Must not be {@code null}. 125 * 126 * @return The authzId string. 127 */ 128 private static String toAuthzIdString(final DN dn) { 129 130 if (dn == null) 131 throw new IllegalArgumentException("The authzId DN must not be null"); 132 133 if (dn.equals(DN.NULL_DN)) 134 return "dn:"; 135 else 136 return "dn:" + dn.toNormalizedString(); 137 } 138 139 140 /** 141 * Returns the authzId string representation of the specified username. 142 * 143 * @param username The username. Must not be {@code null}. 144 * 145 * @return The authzId string. 146 */ 147 private static String toAuthzIdString(final Username username) { 148 149 if (username == null) 150 throw new IllegalArgumentException("The authzId username must not be null"); 151 152 if (username.toString().isEmpty()) 153 return "dn:"; // anonymous 154 155 return "u:" + username; 156 } 157 158 159 /** 160 * Creates a new authorisation identity from the specified DN. 161 * 162 * <p>Note that for the purpose of keying and comparing authorisation 163 * IDs, the DN will be normalised (simple normalisation, without 164 * consulting the schema). 165 * 166 * @param dn The DN, must not be {@code null}. If {@code DN.NULL_DN} 167 * indicates an anonymous user. 168 */ 169 public AuthzId(final DN dn) { 170 171 super(toAuthzIdString(dn)); 172 identityType = IdentityType.DN; 173 this.dn = dn; 174 username = null; 175 } 176 177 178 /** 179 * Creates a new authorisation identity from the specified username. 180 * 181 * <p>Note that for the purpose of keying and comparing authorisation 182 * IDs, the username will be converted to lower case. 183 * 184 * @param username The username, must not be {@code null}. If empty 185 * indicates an anonymous user. 186 */ 187 public AuthzId(final Username username) { 188 189 super(toAuthzIdString(username)); 190 identityType = IdentityType.USERNAME; 191 dn = null; 192 this.username = username; 193 } 194 195 196 /** 197 * Gets the identity type. 198 * 199 * @return The identity type. 200 */ 201 public IdentityType getIdentityType() { 202 203 return identityType; 204 } 205 206 207 /** 208 * Gets the identity DN. 209 * 210 * @return The DN, {@code null} if specified as a username instead. 211 */ 212 public DN getDN() { 213 214 return dn; 215 } 216 217 218 /** 219 * Gets the identity username. 220 * 221 * @return The username, {@code null} if specified as a DN instead. 222 */ 223 public Username getUsername() { 224 225 return username; 226 } 227 228 229 @Override 230 public boolean equals(final Object object) { 231 232 return object instanceof AuthzId && this.toString().equals(object.toString()); 233 } 234}