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.device; 019 020 021import net.jcip.annotations.Immutable; 022 023import com.nimbusds.oauth2.sdk.id.Identifier; 024import com.nimbusds.oauth2.sdk.util.StringUtils; 025 026 027/** 028 * User code. 029 * 030 * <p>Related specifications: 031 * 032 * <ul> 033 * <li>OAuth 2.0 Device Authorization Grant (draft-ietf-oauth-device-flow-15) 034 * </ul> 035 */ 036@Immutable 037public final class UserCode extends Identifier { 038 039 040 public static final String LETTER_CHAR_SET = "BCDFGHJKLMNPQRSTVWXZ"; 041 042 043 public static final String DIGIT_CHAR_SET = "0123456789"; 044 045 046 /** 047 * The character set used by the identifier. The identifier can only 048 * contain characters from this set. 049 */ 050 private final String charset; 051 052 053 /** 054 * Creates a new user code with the specified value. 055 * 056 * @param value The code value. Must not be {@code null} or empty 057 * string. 058 * @param charset The character set used by the identifier. The 059 * identifier can only contain characters from this set. 060 * If {@code null}, all characters are allowed. 061 */ 062 public UserCode(final String value, final String charset) { 063 064 super(value); 065 066 this.charset = charset; 067 } 068 069 070 /** 071 * Creates a new user code with the specified value and the 072 * {@code LETTER_CHAR_SET}. 073 * 074 * @param value The code value. Must not be {@code null} or empty 075 * string. 076 */ 077 public UserCode(final String value) { 078 079 this(value, LETTER_CHAR_SET); 080 } 081 082 083 /** 084 * Creates a new user code with a randomly generated value with 8 085 * characters from {@code LETTER_CHAR_SET}, in the form 086 * {@code WDJB-MJHT}. 087 */ 088 public UserCode() { 089 090 this(LETTER_CHAR_SET, 8); 091 } 092 093 094 /** 095 * Creates a new user code with a randomly generated value from the 096 * specified charset and length. A dash is added every 4 characters. 097 */ 098 public UserCode(final String charset, final int length) { 099 100 this(generateValue(charset, length), charset); 101 } 102 103 104 /** 105 * Creates a new user code with a randomly generated value from the 106 * specified charset and length. A dash is added every 4 characters. 107 * 108 * @param charset The character set used by the identifier. The 109 * identifier can only contain characters from this set. 110 * Must not be {@code null} or empty string. 111 * @param length The length of the value to generate. 112 */ 113 private static String generateValue(final String charset, final int length) { 114 115 if (StringUtils.isBlank(charset)) 116 throw new IllegalArgumentException("The charset must not be null or empty string"); 117 118 StringBuilder value = new StringBuilder(); 119 for (int index = 0; index < length; index++) { 120 if (index > 0 && index % 4 == 0) 121 value.append('-'); 122 value.append(charset.charAt(secureRandom.nextInt(charset.length()))); 123 } 124 return value.toString(); 125 } 126 127 128 /** 129 * Returns the character set used by this {@code UserCode}. 130 * 131 * @return The character set, or {@code null} if unspecified. 132 */ 133 public String getCharset() { 134 135 return charset; 136 } 137 138 139 /** 140 * Returns the value with all invalid characters removed. 141 * 142 * @return The value with all invalid characters removed. 143 */ 144 public String getStrippedValue() { 145 146 return stripIllegalChars(getValue(), getCharset()); 147 } 148 149 150 @Override 151 public int compareTo(final Identifier other) { 152 153 // fallback to default compare for other identifiers 154 if (!(other instanceof UserCode)) 155 return super.compareTo(other); 156 157 return getStrippedValue().compareTo(((UserCode) other).getStrippedValue()); 158 } 159 160 161 @Override 162 public int hashCode() { 163 164 return getStrippedValue() != null ? getStrippedValue().hashCode() : 0; 165 } 166 167 168 @Override 169 public boolean equals(final Object object) { 170 171 return object instanceof UserCode 172 && this.getStrippedValue().equals(((UserCode) object).getStrippedValue()); 173 } 174 175 176 /** 177 * Removes all characters from {@code value} that are not in 178 * {@code charset}. 179 * 180 * @param value The code value. 181 * @param charset The allowed characters in {@code value}. If 182 * {@code null} all characters are retained. 183 * @return The {@code value} with all invalid characters removed. 184 */ 185 public static String stripIllegalChars(final String value, final String charset) { 186 187 if (charset == null) 188 return value.toUpperCase(); 189 190 StringBuilder newValue = new StringBuilder(); 191 for (char curChar : value.toUpperCase().toCharArray()) { 192 if (charset.indexOf(curChar) >= 0) 193 newValue.append(curChar); 194 } 195 return newValue.toString(); 196 } 197}