001package com.nimbusds.oauth2.sdk.id;
002
003
004import java.security.SecureRandom;
005
006import org.apache.commons.lang3.StringUtils;
007
008import org.apache.commons.codec.binary.Base64;
009
010import net.minidev.json.JSONAware;
011import net.minidev.json.JSONValue;
012
013
014/**
015 * The base abstract class for representing identifiers and identities. 
016 * Provides constructors that generate Base64URL-encoded secure random 
017 * identifier values.
018 *
019 * <p>Extending classes must override the {@link #equals} method.
020 *
021 * @author Vladimir Dzhuvinov
022 */
023public abstract class Identifier implements JSONAware {
024        
025        
026        /**
027         * The default byte length of generated identifiers.
028         */
029        public static final int DEFAULT_BYTE_LENGTH = 32;
030        
031        
032        /**
033         * The secure random generator.
034         */
035        private static final SecureRandom secureRandom = new SecureRandom();
036
037
038        /**
039         * The identifier value.
040         */
041        private final String value;
042
043
044        /**
045         * Creates a new identifier with the specified value.
046         *
047         * @param value The identifier value. Must not be {@code null} or empty
048         *              string.
049         */
050        public Identifier(final String value) {
051
052                if (StringUtils.isBlank(value))
053                        throw new IllegalArgumentException("The value must not be null or empty string");
054
055                this.value = value;
056        }
057
058
059        /**
060         * Creates a new identifier with a randomly generated value of the 
061         * specified byte length, Base64URL-encoded.
062         *
063         * @param byteLength The byte length of the value to generate. Must be
064         *                   greater than one.
065         */
066        public Identifier(final int byteLength) {
067                
068                if (byteLength < 1)
069                        throw new IllegalArgumentException("The byte length must be a positive integer");
070                
071                byte[] n = new byte[byteLength];
072                
073                secureRandom.nextBytes(n);
074
075                value = Base64.encodeBase64URLSafeString(n);
076        }
077        
078        
079        /**
080         * Creates a new identifier with a randomly generated 256-bit 
081         * (32-byte) value, Base64URL-encoded.
082         */
083        public Identifier() {
084
085                this(DEFAULT_BYTE_LENGTH);
086        }
087
088
089        /**
090         * Returns the value of this identifier.
091         *
092         * @return The value.
093         */
094        public String getValue() {
095
096                return value;
097        }
098
099
100        /**
101         * Returns the JSON string representation of this identifier.
102         *
103         * @return The JSON string.
104         */
105        @Override
106        public String toJSONString() {
107
108                StringBuilder sb = new StringBuilder("\"");
109                sb.append(JSONValue.escape(value));
110                sb.append('"');
111                return sb.toString();
112        }
113        
114        
115        /**
116         * @see #getValue
117         */
118        @Override
119        public String toString() {
120        
121                return getValue();
122        }
123
124
125        /**
126         * Overrides {@code Object.hashCode()}.
127         *
128         * @return The object hash code.
129         */
130        @Override
131        public int hashCode() {
132        
133                return value.hashCode();
134        }
135        
136        
137        /**
138         * Overrides {@code Object.equals()}.
139         *
140         * @param object The object to compare to.
141         *
142         * @return {@code true} if the objects have the same value, otherwise
143         *         {@code false}.
144         */
145        @Override
146        public abstract boolean equals(final Object object);
147}