|
@@ -0,0 +1,218 @@
|
|
|
+/**
|
|
|
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+ * you may not use this file except in compliance with the License.
|
|
|
+ * You may obtain a copy of the License at
|
|
|
+ *
|
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+ *
|
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+ * See the License for the specific language governing permissions and
|
|
|
+ * limitations under the License. See accompanying LICENSE file.
|
|
|
+ */
|
|
|
+package org.apache.hadoop.security.authentication.util;
|
|
|
+
|
|
|
+import org.apache.hadoop.security.authentication.client.AuthenticationException;
|
|
|
+
|
|
|
+import java.security.Principal;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.StringTokenizer;
|
|
|
+
|
|
|
+/**
|
|
|
+ */
|
|
|
+public class AuthToken implements Principal {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Constant that identifies an anonymous request.
|
|
|
+ */
|
|
|
+
|
|
|
+ private static final String ATTR_SEPARATOR = "&";
|
|
|
+ private static final String USER_NAME = "u";
|
|
|
+ private static final String PRINCIPAL = "p";
|
|
|
+ private static final String EXPIRES = "e";
|
|
|
+ private static final String TYPE = "t";
|
|
|
+
|
|
|
+ private final static Set<String> ATTRIBUTES =
|
|
|
+ new HashSet<String>(Arrays.asList(USER_NAME, PRINCIPAL, EXPIRES, TYPE));
|
|
|
+
|
|
|
+ private String userName;
|
|
|
+ private String principal;
|
|
|
+ private String type;
|
|
|
+ private long expires;
|
|
|
+ private String tokenStr;
|
|
|
+
|
|
|
+ protected AuthToken() {
|
|
|
+ userName = null;
|
|
|
+ principal = null;
|
|
|
+ type = null;
|
|
|
+ expires = -1;
|
|
|
+ tokenStr = "ANONYMOUS";
|
|
|
+ generateToken();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final String ILLEGAL_ARG_MSG = " is NULL, empty or contains a '" + ATTR_SEPARATOR + "'";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Creates an authentication token.
|
|
|
+ *
|
|
|
+ * @param userName user name.
|
|
|
+ * @param principal principal (commonly matches the user name, with Kerberos is the full/long principal
|
|
|
+ * name while the userName is the short name).
|
|
|
+ * @param type the authentication mechanism name.
|
|
|
+ * (<code>System.currentTimeMillis() + validityPeriod</code>).
|
|
|
+ */
|
|
|
+ public AuthToken(String userName, String principal, String type) {
|
|
|
+ checkForIllegalArgument(userName, "userName");
|
|
|
+ checkForIllegalArgument(principal, "principal");
|
|
|
+ checkForIllegalArgument(type, "type");
|
|
|
+ this.userName = userName;
|
|
|
+ this.principal = principal;
|
|
|
+ this.type = type;
|
|
|
+ this.expires = -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Check if the provided value is invalid. Throw an error if it is invalid, NOP otherwise.
|
|
|
+ *
|
|
|
+ * @param value the value to check.
|
|
|
+ * @param name the parameter name to use in an error message if the value is invalid.
|
|
|
+ */
|
|
|
+ protected static void checkForIllegalArgument(String value, String name) {
|
|
|
+ if (value == null || value.length() == 0 || value.contains(ATTR_SEPARATOR)) {
|
|
|
+ throw new IllegalArgumentException(name + ILLEGAL_ARG_MSG);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Sets the expiration of the token.
|
|
|
+ *
|
|
|
+ * @param expires expiration time of the token in milliseconds since the epoch.
|
|
|
+ */
|
|
|
+ public void setExpires(long expires) {
|
|
|
+ this.expires = expires;
|
|
|
+ generateToken();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns true if the token has expired.
|
|
|
+ *
|
|
|
+ * @return true if the token has expired.
|
|
|
+ */
|
|
|
+ public boolean isExpired() {
|
|
|
+ return getExpires() != -1 && System.currentTimeMillis() > getExpires();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Generates the token.
|
|
|
+ */
|
|
|
+ private void generateToken() {
|
|
|
+ StringBuffer sb = new StringBuffer();
|
|
|
+ sb.append(USER_NAME).append("=").append(getUserName()).append(ATTR_SEPARATOR);
|
|
|
+ sb.append(PRINCIPAL).append("=").append(getName()).append(ATTR_SEPARATOR);
|
|
|
+ sb.append(TYPE).append("=").append(getType()).append(ATTR_SEPARATOR);
|
|
|
+ sb.append(EXPIRES).append("=").append(getExpires());
|
|
|
+ tokenStr = sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the user name.
|
|
|
+ *
|
|
|
+ * @return the user name.
|
|
|
+ */
|
|
|
+ public String getUserName() {
|
|
|
+ return userName;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the principal name (this method name comes from the JDK {@link Principal} interface).
|
|
|
+ *
|
|
|
+ * @return the principal name.
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String getName() {
|
|
|
+ return principal;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the authentication mechanism of the token.
|
|
|
+ *
|
|
|
+ * @return the authentication mechanism of the token.
|
|
|
+ */
|
|
|
+ public String getType() {
|
|
|
+ return type;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the expiration time of the token.
|
|
|
+ *
|
|
|
+ * @return the expiration time of the token, in milliseconds since Epoc.
|
|
|
+ */
|
|
|
+ public long getExpires() {
|
|
|
+ return expires;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the string representation of the token.
|
|
|
+ * <p>
|
|
|
+ * This string representation is parseable by the {@link #parse} method.
|
|
|
+ *
|
|
|
+ * @return the string representation of the token.
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ return tokenStr;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static AuthToken parse(String tokenStr) throws AuthenticationException {
|
|
|
+ if (tokenStr.length() >= 2) {
|
|
|
+ // strip the \" at the two ends of the tokenStr
|
|
|
+ if (tokenStr.charAt(0) == '\"' &&
|
|
|
+ tokenStr.charAt(tokenStr.length()-1) == '\"') {
|
|
|
+ tokenStr = tokenStr.substring(1, tokenStr.length()-1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Map<String, String> map = split(tokenStr);
|
|
|
+ // remove the signature part, since client doesn't care about it
|
|
|
+ map.remove("s");
|
|
|
+
|
|
|
+ if (!map.keySet().equals(ATTRIBUTES)) {
|
|
|
+ throw new AuthenticationException("Invalid token string, missing attributes");
|
|
|
+ }
|
|
|
+ long expires = Long.parseLong(map.get(EXPIRES));
|
|
|
+ AuthToken token = new AuthToken(map.get(USER_NAME), map.get(PRINCIPAL), map.get(TYPE));
|
|
|
+ token.setExpires(expires);
|
|
|
+ return token;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Splits the string representation of a token into attributes pairs.
|
|
|
+ *
|
|
|
+ * @param tokenStr string representation of a token.
|
|
|
+ *
|
|
|
+ * @return a map with the attribute pairs of the token.
|
|
|
+ *
|
|
|
+ * @throws AuthenticationException thrown if the string representation of the token could not be broken into
|
|
|
+ * attribute pairs.
|
|
|
+ */
|
|
|
+ private static Map<String, String> split(String tokenStr) throws AuthenticationException {
|
|
|
+ Map<String, String> map = new HashMap<String, String>();
|
|
|
+ StringTokenizer st = new StringTokenizer(tokenStr, ATTR_SEPARATOR);
|
|
|
+ while (st.hasMoreTokens()) {
|
|
|
+ String part = st.nextToken();
|
|
|
+ int separator = part.indexOf('=');
|
|
|
+ if (separator == -1) {
|
|
|
+ throw new AuthenticationException("Invalid authentication token");
|
|
|
+ }
|
|
|
+ String key = part.substring(0, separator);
|
|
|
+ String value = part.substring(separator + 1);
|
|
|
+ map.put(key, value);
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|