/* * 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.geode.security.generator; import java.security.Principal; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.logging.log4j.Logger; import org.apache.geode.internal.logging.LogService; import org.apache.geode.security.AuthInitialize; import org.apache.geode.security.Authenticator; import org.apache.geode.security.templates.DummyAuthenticator; import org.apache.geode.security.templates.LdapUserAuthenticator; import org.apache.geode.security.templates.PKCSAuthenticator; /** * Encapsulates obtaining valid and invalid credentials. Implementations will be for different kinds * of authentication schemes. * * @since GemFire 5.5 */ public abstract class CredentialGenerator { private static final Logger logger = LogService.getLogger(); /** * A set of properties that should be added to the Gemfire system properties before using the * authentication module. */ private Properties systemProperties = null; /** * A set of properties that should be added to the java system properties before using the * authentication module. */ protected Properties javaProperties = null; /** * A factory method to create a new instance of an {@link CredentialGenerator} for the given * {@link ClassCode}. Caller is supposed to invoke {@link CredentialGenerator#init} immediately * after obtaining the instance. * * @param classCode the {@code ClassCode} of the {@code CredentialGenerator} implementation * * @return an instance of {@code CredentialGenerator} for the given class code */ public static CredentialGenerator create(final ClassCode classCode) { switch (classCode.classType) { // Removing dummy one to reduce test run times // case ClassCode.ID_DUMMY: // return new DummyCredentialGenerator(); case ClassCode.ID_LDAP: return new LdapUserCredentialGenerator(); // case ClassCode.ID_SSL:ΓΈ // return new SSLCredentialGenerator(); case ClassCode.ID_PKCS: return new PKCSCredentialGenerator(); default: return null; } } /** * Initialize the credential generator. * * @throws IllegalArgumentException when there is a problem during initialization */ public void init() throws IllegalArgumentException { this.systemProperties = initialize(); logger.info("Generating CredentialGenerator with {}", this.systemProperties); } /** * @return A set of extra properties that should be added to Gemfire system properties when not * null. */ public Properties getSystemProperties() { return this.systemProperties; } /** * @return A set of extra properties that should be added to Gemfire system properties when not * null. */ public Properties getJavaProperties() { return this.javaProperties; } /** * The {@link ClassCode} of this particular implementation. * * @return the {@code ClassCode} */ public abstract ClassCode classCode(); /** * The name of the {@link AuthInitialize} factory function that should be used in conjunction with * the credentials generated by this generator. * * @return name of the {@code AuthInitialize} factory function */ public abstract String getAuthInit(); /** * The name of the {@link Authenticator} factory function that should be used in conjunction with * the credentials generated by this generator. * * @return name of the {@code Authenticator} factory function */ public abstract String getAuthenticator(); /** * Get a set of valid credentials generated using the given index. */ public abstract Properties getValidCredentials(final int index); /** * Get a set of valid credentials for the given {@link Principal}. * * @return credentials for the given {@code Principal} or null if none possible. */ public abstract Properties getValidCredentials(final Principal principal); /** * Get a set of invalid credentials generated using the given index. */ public abstract Properties getInvalidCredentials(final int index); /** * Initialize the credential generator. This is provided separately from the {@link #init()} * method for convenience of implementations so that they do not need to store in * {@link #systemProperties}. The latter is convenient for the users who do not need to store * these properties rather can obtain it later by invoking {@link #getSystemProperties()} * * <p> * Required to be implemented by concrete classes that implement this abstract class. * * @return A set of extra properties that should be added to Gemfire system properties when not * null. * * @throws IllegalArgumentException when there is a problem during initialization */ protected abstract Properties initialize() throws IllegalArgumentException; /** * Enumeration for various {@link CredentialGenerator} implementations. * * <p> * The following schemes are supported as of now: {@code DummyAuthenticator}, * {@code LdapUserAuthenticator}, {@code PKCSAuthenticator}. In addition SSL socket mode with * mutual authentication is also supported. * * <p> * To add a new authentication scheme the following needs to be done: * <ul> * <li>Add implementations for {@link AuthInitialize} and {@link Authenticator} classes for * clients/peers.</li> * <li>Add a new enumeration value for the scheme in this class. Notice the size of {@code VALUES} * array and increase that if it is getting overflowed. Note the methods and fields for existing * schemes and add for the new one in a similar manner.</li> * <li>Add an implementation for {@link CredentialGenerator}.</li> * <li>Modify the CredentialGenerator.Factory#create [no such Factory exists] method to add * creation of an instance of the new implementation for the {@code ClassCode} enumeration * value.</li> * </ul> * * <p> * All security dunit tests will automagically start testing the new implementation after this. * * @since GemFire 5.5 */ public static final class ClassCode { private static byte nextOrdinal = 0; private static final byte ID_DUMMY = 1; private static final byte ID_LDAP = 2; private static final byte ID_PKCS = 3; private static final byte ID_SSL = 4; private static final ClassCode[] VALUES = new ClassCode[10]; private static final Map CODE_NAME_MAP = new HashMap(); public static final ClassCode DUMMY = new ClassCode(DummyAuthenticator.class.getName() + ".create", ID_DUMMY); public static final ClassCode LDAP = new ClassCode(LdapUserAuthenticator.class.getName() + ".create", ID_LDAP); public static final ClassCode PKCS = new ClassCode(PKCSAuthenticator.class.getName() + ".create", ID_PKCS); public static final ClassCode SSL = new ClassCode("SSL", ID_SSL); /** The name of this class. */ private final String name; /** byte used as ordinal to represent this class */ private final byte ordinal; /** * One of the following: ID_DUMMY, ID_LDAP, ID_PKCS */ private final byte classType; /** Creates a new instance of class code. */ private ClassCode(final String name, final byte classType) { this.name = name; this.classType = classType; this.ordinal = nextOrdinal++; VALUES[this.ordinal] = this; CODE_NAME_MAP.put(name, this); } public boolean isDummy() { return this.classType == ID_DUMMY; } public boolean isLDAP() { return this.classType == ID_LDAP; } public boolean isPKCS() { return this.classType == ID_PKCS; } public boolean isSSL() { return this.classType == ID_SSL; } /** * Returns the {@code ClassCode} represented by specified ordinal. */ public static ClassCode fromOrdinal(final byte ordinal) { return VALUES[ordinal]; } /** * Returns the {@code ClassCode} represented by specified string. */ public static ClassCode parse(final String operationName) { return (ClassCode) CODE_NAME_MAP.get(operationName); } /** * Returns all the possible values. */ public static List getAll() { final List codes = new ArrayList(); for (Iterator iter = CODE_NAME_MAP.values().iterator(); iter.hasNext();) { codes.add(iter.next()); } return codes; } /** * Returns the ordinal for this operation code. * * @return the ordinal of this operation. */ public byte toOrdinal() { return this.ordinal; } /** * Returns a string representation for this operation. * * @return the name of this operation. */ @Override public final String toString() { return this.name; } /** * Indicates whether other object is same as this one. * * @return true if other object is same as this one. */ @Override public final boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof ClassCode)) { return false; } final ClassCode other = (ClassCode) obj; return other.ordinal == this.ordinal; } /** * Indicates whether other {@code ClassCode} is same as this one. * * @return true if other {@code ClassCode} is same as this one. */ public final boolean equals(final ClassCode opCode) { return opCode != null && opCode.ordinal == this.ordinal; } /** * Returns a hash code value for this {@code ClassCode} which is the same as its ordinal. * * @return the ordinal of this operation. */ @Override public final int hashCode() { return this.ordinal; } } }