From f8d855f1a2ced44210e5ef1a38277ccc3662b144 Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Tue, 18 Mar 2008 18:45:38 +0000 Subject: [PATCH] SEC-716: Default (non-web) AuthenticationDetailsSource implementation. --- .../security/ui/AuthenticationDetails.java | 81 +++++++++++++++++++ .../ui/AuthenticationDetailsSourceImpl.java | 81 +++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100755 core/src/main/java/org/springframework/security/ui/AuthenticationDetails.java create mode 100755 core/src/main/java/org/springframework/security/ui/AuthenticationDetailsSourceImpl.java diff --git a/core/src/main/java/org/springframework/security/ui/AuthenticationDetails.java b/core/src/main/java/org/springframework/security/ui/AuthenticationDetails.java new file mode 100755 index 0000000000..831f4a077b --- /dev/null +++ b/core/src/main/java/org/springframework/security/ui/AuthenticationDetails.java @@ -0,0 +1,81 @@ +package org.springframework.security.ui; + +import java.io.Serializable; + +/** +* A holder of the context as a string. +* +* @author Ruud Senden +* @since 2.0 +*/ +public class AuthenticationDetails implements Serializable { + //~ Instance fields ================================================================================================ + + private String context; + + //~ Constructors =================================================================================================== + + /** + * Constructor. + * + * @param context that the authentication request is initiated from + */ + public AuthenticationDetails(Object context) { + this.context = context==null?"":context.toString(); + doPopulateAdditionalInformation(context); + } + + protected AuthenticationDetails() { + throw new IllegalArgumentException("Cannot use default constructor"); + } + + //~ Methods ======================================================================================================== + + /** + * Provided so that subclasses can populate additional information. + * + * @param request that the authentication request was received from + */ + protected void doPopulateAdditionalInformation(Object context) {} + + public boolean equals(Object obj) { + if (obj instanceof AuthenticationDetails) { + AuthenticationDetails rhs = (AuthenticationDetails) obj; + + if ((context == null) && (rhs.getContext() != null)) { + return false; + } + + if ((context != null) && (rhs.getContext() == null)) { + return false; + } + + if (context != null) { + if (!context.equals(rhs.getContext())) { + return false; + } + } + + return true; + } + + return false; + } + + /** + * Indicates the context. + * + * @return the address + */ + public String getContext() { + return context; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(super.toString() + ": "); + sb.append("Context: " + this.getContext()); + + return sb.toString(); + } +} diff --git a/core/src/main/java/org/springframework/security/ui/AuthenticationDetailsSourceImpl.java b/core/src/main/java/org/springframework/security/ui/AuthenticationDetailsSourceImpl.java new file mode 100755 index 0000000000..2508cd6abb --- /dev/null +++ b/core/src/main/java/org/springframework/security/ui/AuthenticationDetailsSourceImpl.java @@ -0,0 +1,81 @@ +package org.springframework.security.ui; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import org.springframework.security.ui.AuthenticationDetailsSource; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; + +/** + * Base implementation of {@link AuthenticationDetailsSource}. + *

+ * By default will create an instance of AuthenticationDetails. + * Any object that accepts an Object as its sole constructor can + * be used instead of this default. + *

+ * + * @author Ruud Senden + * @since 2.0 + */ +public class AuthenticationDetailsSourceImpl implements AuthenticationDetailsSource { + //~ Instance fields ================================================================================================ + + private Class clazz = AuthenticationDetails.class; + + //~ Methods ======================================================================================================== + + public Object buildDetails(Object context) { + try { + Constructor constructor = getFirstMatchingConstructor(context); + return constructor.newInstance(new Object[] { context }); + } catch (NoSuchMethodException ex) { + ReflectionUtils.handleReflectionException(ex); + } catch (InvocationTargetException ex) { + ReflectionUtils.handleReflectionException(ex); + } catch (InstantiationException ex) { + ReflectionUtils.handleReflectionException(ex); + } catch (IllegalAccessException ex) { + ReflectionUtils.handleReflectionException(ex); + } + + return null; + } + + /** + * Return the first matching constructor that can take the given object + * as an argument. Please note that we cannot use + * getDeclaredConstructor(new Class[]{object.getClass()}) + * as this will only match if the constructor argument type matches + * the object type exactly (instead of checking whether it is assignable) + * + * @param object the object for which to find a matching constructor + * @return a matching constructor for the given object + * @throws NoSuchMethodException if no matching constructor can be found + */ + private Constructor getFirstMatchingConstructor(Object object) throws NoSuchMethodException { + Constructor[] constructors = clazz.getDeclaredConstructors(); + Constructor constructor = null; + for (int i = 0; i < constructors.length; i++) { + Class[] parameterTypes = constructors[i].getParameterTypes(); + if (parameterTypes.length == 1 && (object == null || parameterTypes[i].isInstance(object))) { + constructor = constructors[i]; + break; + } + } + + if (constructor == null) { + if (object == null) { + throw new NoSuchMethodException("No constructor found that can take a single argument"); + } else { + throw new NoSuchMethodException("No constructor found that can take a single argument of type " + object.getClass()); + } + } + return constructor; + } + + public void setClazz(Class clazz) { + Assert.notNull(clazz, "Class required"); + this.clazz = clazz; + } +}