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}