| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| package org.apache.pulsar.broker.authentication; |
| |
| 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; |
| |
| import javax.naming.AuthenticationException; |
| |
| import lombok.extern.slf4j.Slf4j; |
| |
| @Slf4j |
| public class SaslRoleToken implements Principal { |
| |
| /** |
| * Constant that identifies an anonymous request. |
| */ |
| public static final SaslRoleToken ANONYMOUS = new SaslRoleToken(); |
| |
| private static final String ATTR_SEPARATOR = "&"; |
| private static final String USER_ROLE = "u"; |
| private static final String EXPIRES = "e"; |
| private static final String SESSION = "i"; |
| |
| private static final Set<String> ATTRIBUTES = |
| new HashSet<String>(Arrays.asList(USER_ROLE, EXPIRES, SESSION)); |
| |
| private String userRole; |
| private String session; |
| private long expires; |
| private String token; |
| |
| private SaslRoleToken() { |
| userRole = null; |
| session = null; |
| expires = -1; |
| token = "ANONYMOUS"; |
| generateToken(); |
| } |
| |
| private static final String ILLEGAL_ARG_MSG = " is NULL, empty or contains a '" + ATTR_SEPARATOR + "'"; |
| |
| /** |
| * Creates an authentication token. |
| * |
| * @param userRole user name. |
| * @param session the sessionId. |
| * (<code>System.currentTimeMillis() + validityPeriod</code>). |
| */ |
| public SaslRoleToken(String userRole, String session) { |
| checkForIllegalArgument(session, "session"); |
| this.userRole = userRole; |
| this.session = session; |
| this.expires = -1; |
| generateToken(); |
| } |
| |
| public SaslRoleToken(String userRole, String session, long expires) { |
| checkForIllegalArgument(userRole, "userRole"); |
| checkForIllegalArgument(session, "session"); |
| this.userRole = userRole; |
| this.session = session; |
| this.expires = expires; |
| generateToken(); |
| } |
| |
| /** |
| * 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. |
| */ |
| private 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) { |
| if (this != SaslRoleToken.ANONYMOUS) { |
| this.expires = expires; |
| generateToken(); |
| } |
| } |
| |
| /** |
| * Generates the token. |
| */ |
| private void generateToken() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(USER_ROLE).append("=").append(getUserRole()).append(ATTR_SEPARATOR); |
| sb.append(SESSION).append("=").append(getSession()).append(ATTR_SEPARATOR); |
| sb.append(EXPIRES).append("=").append(getExpires()); |
| token = sb.toString(); |
| } |
| |
| /** |
| * Returns the user name. |
| * |
| * @return the user name. |
| */ |
| public String getUserRole() { |
| return userRole; |
| } |
| |
| /** |
| * Returns the principal name (this method name comes from the JDK {@link Principal} interface). |
| * |
| * @return the principal name. |
| */ |
| @Override |
| public String getName() { |
| return userRole; |
| } |
| |
| /** |
| * Returns the authentication mechanism of the token. |
| * |
| * @return the authentication mechanism of the token. |
| */ |
| public String getSession() { |
| return session; |
| } |
| |
| /** |
| * Returns the expiration time of the token. |
| * |
| * @return the expiration time of the token, in milliseconds since Epoc. |
| */ |
| public long getExpires() { |
| return expires; |
| } |
| |
| /** |
| * Returns if the token has expired. |
| * |
| * @return if the token has expired. |
| */ |
| public boolean isExpired() { |
| return getExpires() != -1 && System.currentTimeMillis() > getExpires(); |
| } |
| |
| /** |
| * 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 token; |
| } |
| |
| /** |
| * Parses a string into an authentication token. |
| * |
| * @param tokenStr string representation of a token. |
| * |
| * @return the parsed authentication token. |
| * |
| * @throws AuthenticationException thrown if the string representation could not be parsed into |
| * an authentication token. |
| */ |
| public static SaslRoleToken parse(String tokenStr) throws AuthenticationException { |
| Map<String, String> map = split(tokenStr); |
| if (!map.keySet().equals(ATTRIBUTES)) { |
| throw new AuthenticationException("Invalid token string, missing attributes"); |
| } |
| long expires = Long.parseLong(map.get(EXPIRES)); |
| SaslRoleToken token = new SaslRoleToken(map.get(USER_ROLE), map.get(SESSION)); |
| 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; |
| } |
| |
| } |