001    package com.nimbusds.oauth2.sdk.auth;
002    
003    
004    import java.nio.charset.Charset;
005    import java.util.Date;
006    
007    import org.apache.commons.lang3.ArrayUtils;
008    import org.apache.commons.lang3.RandomStringUtils;
009    import org.apache.commons.lang3.StringUtils;
010    
011    
012    /**
013     * Secret or password. The secret should be {@link #erase erased} when no 
014     * longer in use.
015     *
016     * @author Vladimir Dzhuvinov
017     */
018    public class Secret {
019    
020    
021            /**
022             * The secret value.
023             */
024            private byte[] value;
025    
026    
027            /**
028             * Optional expiration date.
029             */
030            private final Date expDate;
031    
032    
033            /**
034             * Creates a new secret with the specified value.
035             *
036             * @param value The value. Must not be {@code null} or empty string.
037             */
038            public Secret(final String value) {
039    
040                    this(value, null);
041            }
042            
043            
044            /**
045             * Creates a new secret with the specified value.
046             *
047             * @param value The value. Must not be {@code null} or empty array.
048             */
049            public Secret(final byte[] value) {
050    
051                    this(value, null);
052            }
053    
054    
055            /**
056             * Creates a new secret with the specified value and expiration date.
057             *
058             * @param value   The value. Must be UTF-8 encoded, not {@code null} or 
059             * *              empty string.
060             * @param expDate The expiration date, {@code null} if not specified.
061             */
062            public Secret(final String value, final Date expDate) {
063    
064                    if (StringUtils.isBlank(value))
065                            throw new IllegalArgumentException("The value must not be null or empty string");
066    
067                    this.value = value.getBytes(Charset.forName("utf-8"));
068    
069                    this.expDate = expDate;
070            }
071            
072            
073            /**
074             * Creates a new secret with the specified value and expiration date.
075             *
076             * @param value   The value. Must not be {@code null} or empty string.
077             * @param expDate The expiration date, {@code null} if not specified.
078             */
079            public Secret(final byte[] value, final Date expDate) {
080    
081                    if (ArrayUtils.isEmpty(value))
082                            throw new IllegalArgumentException("The value must not be null or empty array");
083    
084                    this.value = value;
085                    
086                    this.expDate = expDate;
087            }
088    
089    
090            /**
091             * Creates a new secret with a randomly generated value of the 
092             * specified length. The value will be made up of mixed-case 
093             * alphanumeric ASCII characters.
094             *
095             * @param length The number of characters. Must be a positive integer.
096             */
097            public Secret(final int length) {
098            
099                    this(RandomStringUtils.randomAlphanumeric(length));
100            }
101            
102            
103            /**
104             * Creates a new secret with a randomly generated value. The value will
105             * be made up of 32 mixed-case alphanumeric ASCII characters.
106             */
107            public Secret() {
108    
109                    this(32);
110            }
111    
112    
113            /**
114             * Gets the value of this secret.
115             *
116             * @return The value as a UTF-8 encoded string, {@code null} if it has 
117             *         been erased.
118             */
119            public String getValue() {
120    
121                    if (ArrayUtils.isEmpty(value))
122                            return null;
123                    
124                    return new String(value, Charset.forName("utf-8"));
125            }
126            
127            
128            /**
129             * Gets the value of this secret.
130             *
131             * @return The value as a byte array, {@code null} if it has 
132             *         been erased.
133             */
134            public byte[] getValueBytes() {
135    
136                    return value;
137            }
138    
139    
140            /**
141             * Erases of the value of this secret.
142             */
143            public void erase() {
144    
145                    if (ArrayUtils.isEmpty(value))
146                            return;
147                    
148                    for (int i=0; i < value.length; i++)
149                            value[i] = 0;
150                    
151                    value = null;
152            }
153    
154    
155            /**
156             * Gets the expiration date of this secret.
157             *
158             * @return The expiration date, {@code null} if not specified.
159             */
160            public Date getExpirationDate() {
161    
162                    return expDate;
163            }
164    
165    
166            /**
167             * Checks is this secret has expired.
168             *
169             * @return {@code true} if the secret has an associated expiration date
170             *         which is in the past (according to the current system time), 
171             *         else returns {@code false}.
172             */
173            public boolean expired() {
174    
175                    if (expDate == null)
176                            return false;
177    
178                    final Date now = new Date();
179    
180                    if (expDate.after(now))
181                            return false;
182                    else
183                            return true;
184            }
185    
186            
187            
188            /**
189             * Overrides {@code Object.equals()}.
190             *
191             * @param object The object to compare to.
192             *
193             * @return {@code true} if the objects are secrets the same value, 
194             *         otherwise {@code false}.
195             */
196            @Override
197            public boolean equals(final Object object) {
198            
199                    return object != null && 
200                           object instanceof Secret && 
201                           this.getValue().equals(((Secret)object).getValue());
202            }
203    }