From: Erik Brakkee Date: Sat, 22 Mar 2008 16:32:13 +0000 (+0000) Subject: Moved over some of the security stuff from Photos. X-Git-Tag: wamblee-utils-0.7~835 X-Git-Url: http://wamblee.org/gitweb/?a=commitdiff_plain;h=162af365e45e54e5e8d656be276914df2005eaec;p=utils Moved over some of the security stuff from Photos. --- diff --git a/.classpath b/.classpath index 84317413..40a31d71 100644 --- a/.classpath +++ b/.classpath @@ -4,6 +4,9 @@ + + + @@ -28,7 +31,7 @@ - + diff --git a/pom.xml b/pom.xml index 378e4791..981a892b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,425 +1,437 @@ - 4.0.0 - org.wamblee - wamblee-utils - pom - 0.2-SNAPSHOT - wamblee.org utility libraries - http://wamblee.org - - support - hibernate-jpa - socketproxy - crawler - gps - mythtv - - - - junit - junit - 4.4 - test - - - dbunit - dbunit - 2.1 - test - - - jmock - jmock-cglib - 1.1.0 - test - - - cglib - cglib-full - - - - - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + org.wamblee + wamblee-utils + pom + 0.2-SNAPSHOT + wamblee.org utility libraries + http://wamblee.org + + support + hibernate-jpa + security + socketproxy + crawler + gps + mythtv + - - org.wamblee - wamblee-support - ${project.version} - - - org.wamblee - wamblee-support - test-jar - ${project.version} - - - org.wamblee - wamblee-hibernate-jpa - ${project.version} - - - org.wamblee - wamblee-hibernate-jpa - ${project.version} - test-jar - - - org.wamblee - wamblee-socketproxy - ${project.version} - - - org.wamblee - wamblee-socketproxy - test-jar - ${project.version} - - - org.wamblee - wamblee-crawler - ${project.version} - - - org.wamblee - wamblee-crawler - test-jar - ${project.version} - - - org.wamblee - wamblee-crawler-basic - ${project.version} - - - org.wamblee - wamblee-crawler-basic - test-jar - ${project.version} - + + junit + junit + 4.4 + test + + + org.dbunit + dbunit + 2.2 + test + + + jmock + jmock-cglib + 1.1.0 + test + + + cglib + cglib-full + + + + - - javax.persistence - persistence-api - 1.0 - provided - - - javax.activation - activation - 1.1 - - - javax.mail - mail - 1.4.1 - - - javax.servlet - servlet-api - 2.3 - jar - provided - - - org.apache.derby - derby - 10.3.2.1 - - - org.apache.derby - derbyclient - 10.3.2.1 - - - org.apache.derby - derbynet - 10.3.2.1 - - - quartz - quartz - 1.5.1 - - - jtidy - jtidy - 4aug2000r7-dev - + + + + org.wamblee + wamblee-support + ${project.version} + + + org.wamblee + wamblee-support + test-jar + ${project.version} + + + org.wamblee + wamblee-hibernate-jpa + ${project.version} + + + org.wamblee + wamblee-hibernate-jpa + ${project.version} + test-jar + + + org.wamblee + wamblee-socketproxy + ${project.version} + + + org.wamblee + wamblee-socketproxy + test-jar + ${project.version} + + + org.wamblee + wamblee-crawler + ${project.version} + + + org.wamblee + wamblee-crawler + test-jar + ${project.version} + + + org.wamblee + wamblee-crawler-basic + ${project.version} + + + org.wamblee + wamblee-crawler-basic + test-jar + ${project.version} + - - - commons-logging - commons-logging - 1.0.2 - - - commons-httpclient - commons-httpclient - 3.0 - - - commons-beanutils - commons-beanutils - 1.7.0 - - - org.springframework - spring-beans - ${springversion} - - - org.springframework - spring-web - ${springversion} - - - org.springframework - spring-jms - ${springversion} - - - org.springframework - spring-context - ${springversion} - - - org.springframework - spring-hibernate3 - ${springversion} - - - org.springframework - spring-jpa - ${springversion} - - - org.springframework - spring-aop - ${springversion} - + + javax.persistence + persistence-api + 1.0 + provided + + + javax.activation + activation + 1.1 + + + javax.mail + mail + 1.4.1 + + + javax.servlet + servlet-api + 2.3 + jar + provided + + + mysql + mysql-connector-java + 3.1.14 + + + org.apache.derby + derby + 10.3.2.1 + + + org.apache.derby + derbyclient + 10.3.2.1 + + + org.apache.derby + derbynet + 10.3.2.1 + + + quartz + quartz + 1.5.1 + + + jtidy + jtidy + 4aug2000r7-dev + - - - log4j - log4j - 1.2.8 - - - dom4j - dom4j - 1.6 - - - xml-apis - xml-apis - - - - - net.sf.ehcache - ehcache - 1.2.3 - - - xerces - xercesImpl - 2.8.1 - - - - - commons-email - commons-email - 1.0 - - - jaxen - jaxen - 1.1-beta-9 - - - xom - xom - - - xerces - xmlParserAPIs - - - - - jstl - jstl - 1.1.2 - - - taglibs - standard - 1.1.2 - - - jfree - jfreechart - 1.0.1 - - - jfree - jcommon - 1.0.2 - - - - javaee - javaee-api - 5 - provided - + + commons-logging + commons-logging + 1.0.2 + + + commons-httpclient + commons-httpclient + 3.0 + + + commons-beanutils + commons-beanutils + 1.7.0 + + + org.springframework + spring-beans + ${springversion} + + + org.springframework + spring-web + ${springversion} + + + org.springframework + spring-jms + ${springversion} + + + org.springframework + spring-context + ${springversion} + + + org.springframework + spring-hibernate3 + ${springversion} + + + org.springframework + spring-jpa + ${springversion} + + + org.springframework + spring-aop + ${springversion} + - - + + + log4j + log4j + 1.2.8 + + + dom4j + dom4j + 1.6 + + + xml-apis + xml-apis + + + + + net.sf.ehcache + ehcache + 1.2.3 + + + xerces + xercesImpl + 2.8.1 + + + + + commons-email + commons-email + 1.0 + + + jaxen + jaxen + 1.1-beta-9 + + + xom + xom + + + xerces + xmlParserAPIs + + + + + jstl + jstl + 1.1.2 + + + taglibs + standard + 1.1.2 + + + jfree + jfreechart + 1.0.1 + + + jfree + jcommon + 1.0.2 + - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.6 - 1.6 - - + + javaee + javaee-api + 5 + provided + - - org.apache.maven.plugins - maven-surefire-plugin - - - **/*Test.java - - - + + commons-codec + commons-codec + 1.3 + - - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + + org.codehaus.mojo + cobertura-maven-plugin + + + + clean + + + + - - org.codehaus.mojo - cobertura-maven-plugin - - - - clean - - - - + - + - + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + checkstyle + javadoc + dependencies + project-team + mailing-list + issue-tracking + license + scm + + + + + + org.codehaus.mojo + changes-maven-plugin + 2.0-beta-1 + + + + changes-report + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.codehaus.mojo + surefire-report-maven-plugin + + + + org.codehaus.mojo + cobertura-maven-plugin + - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - - - - checkstyle - javadoc - dependencies - project-team - mailing-list - issue-tracking - license - scm - - - - - - org.codehaus.mojo - changes-maven-plugin - 2.0-beta-1 - - - - changes-report - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - org.codehaus.mojo - surefire-report-maven-plugin - - - - org.codehaus.mojo - cobertura-maven-plugin - + + + org.apache.maven.plugins + maven-checkstyle-plugin + + config/sun_checks.xml + + - - - org.apache.maven.plugins - maven-checkstyle-plugin - - config/sun_checks.xml - - + + + org.codehaus.mojo + taglist-maven-plugin + + + TODO + @todo + FIXME + + + - - - org.codehaus.mojo - taglist-maven-plugin - - - TODO - @todo - FIXME - - - + + - - - - - - javaee - Java EE repo at SUN - http://download.java.net/maven/1 - legacy - - + + + javaee + Java EE repo at SUN + http://download.java.net/maven/1 + legacy + + - - 2.0.8 - + + 2.0.8 + diff --git a/security/pom.xml b/security/pom.xml new file mode 100644 index 00000000..9d6264bf --- /dev/null +++ b/security/pom.xml @@ -0,0 +1,69 @@ + + + + org.wamblee + wamblee-utils + 0.2-SNAPSHOT + + + 4.0.0 + org.wamblee + wamblee-security + jar + wamblee.org security + http://wamblee.org + + + + org.wamblee + wamblee-support + + + org.wamblee + wamblee-support + test-jar + + + org.wamblee + wamblee-hibernate-jpa + + + + commons-codec + commons-codec + + + mysql + mysql-connector-java + + + + org.springframework + spring-beans + + + org.springframework + spring-hibernate3 + + + org.hibernate + hibernate + + + + + org.springframework + spring-aop + + + javax.transaction + transaction-api + 1.1 + test + + + + + diff --git a/security/src/main/java/org/wamblee/security/authorization/AllOperation.java b/security/src/main/java/org/wamblee/security/authorization/AllOperation.java new file mode 100644 index 00000000..c76d00ca --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/AllOperation.java @@ -0,0 +1,55 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +/** + * An superclass of all other operations. + */ +public class AllOperation implements Operation { + + private static final String OPERATION = "all"; + + /** + * Operation name. + */ + private String _name; + + /** + * Constructs an all operation. + * + */ + public AllOperation() { + _name = OPERATION; + } + + /** + * Constructs the operation, this constructor is the one that must be used + * by subclasses. + * @param aName Name of the operation. This name must be unique among all operations. + */ + protected AllOperation(String aName) { + _name = aName; + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.Operation#getName() + */ + public String getName() { + return _name; + } + +} diff --git a/security/src/main/java/org/wamblee/security/authorization/AnyUserCondition.java b/security/src/main/java/org/wamblee/security/authorization/AnyUserCondition.java new file mode 100644 index 00000000..c67855d0 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/AnyUserCondition.java @@ -0,0 +1,50 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import org.wamblee.persistence.AbstractPersistent; +import org.wamblee.usermgt.User; + +/** + * Matches any user. + */ +public class AnyUserCondition extends AbstractPersistent implements UserCondition { + + /** + * Constructs the condition. + * + */ + public AnyUserCondition() { + // Empty. + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.UserCondition#matches(org.wamblee.usermgt.User) + */ + public boolean matches(User aUser) { + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "AnyUserCondition()"; + } + +} diff --git a/security/src/main/java/org/wamblee/security/authorization/AuthorizationException.java b/security/src/main/java/org/wamblee/security/authorization/AuthorizationException.java new file mode 100644 index 00000000..a44e2dfc --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/AuthorizationException.java @@ -0,0 +1,42 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +/** + * Authorization exception to be thrown when + * a resouce may not be accessed. + */ +public class AuthorizationException extends RuntimeException { + + private Object _resource; + private Operation _operation; + + public AuthorizationException(Object aResource, Operation aOperation) { + super("Operation '" + aOperation + "' not allowed on '" + aResource + "'"); + _resource = aResource; + _operation = aOperation; + } + + public Object getResource() { + return _resource; + } + + public Operation getOperation() { + return _operation; + } + +} diff --git a/security/src/main/java/org/wamblee/security/authorization/AuthorizationInitializer.java b/security/src/main/java/org/wamblee/security/authorization/AuthorizationInitializer.java new file mode 100644 index 00000000..7423eab9 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/AuthorizationInitializer.java @@ -0,0 +1,43 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import org.apache.log4j.Logger; + + +/** + * Inititializer class for authorization rules. This class initializes the + * authorization rules in case none are present. + */ +public class AuthorizationInitializer { + + private static final Logger LOGGER = Logger.getLogger(AuthorizationInitializer.class); + + /** + * Initializes authorization rules in case none are present. + * @param aService Authorization service. + * @param aRules Default rules for initialization. + */ + public AuthorizationInitializer(AuthorizationService aService, AuthorizationRule[] aRules) { + if ( aService.getRules().length == 0 ) { + for (AuthorizationRule rule: aRules) { + LOGGER.info("Appending authorization rule " + rule); + aService.appendRule(rule); + } + } + } +} diff --git a/security/src/main/java/org/wamblee/security/authorization/AuthorizationResult.java b/security/src/main/java/org/wamblee/security/authorization/AuthorizationResult.java new file mode 100644 index 00000000..f36bf490 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/AuthorizationResult.java @@ -0,0 +1,42 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +/** + * Represents the result of an authorization decision. + */ +public enum AuthorizationResult { + /** + * Access is granted. + */ + GRANTED, + + /** + * Access is denied. + */ + DENIED, + + /** + * Access is undecided. + */ + UNDECIDED, + + /** + * Unsupported resource. + */ + UNSUPPORTED_RESOURCE +} diff --git a/security/src/main/java/org/wamblee/security/authorization/AuthorizationRule.java b/security/src/main/java/org/wamblee/security/authorization/AuthorizationRule.java new file mode 100644 index 00000000..8c17b7f3 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/AuthorizationRule.java @@ -0,0 +1,44 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import org.wamblee.persistence.Persistent; +import org.wamblee.usermgt.User; + +/** + * Represents an authorization rule to determine whether an operation is allowed on a resource. + */ +public interface AuthorizationRule extends Persistent { + + /** + * Returns the supported object types for which this authorization rule applies. + * This can be used by the authorization service for optimization. + * @return Array of supported types. + */ + Class[] getSupportedTypes(); + + /** + * Determines whether an operation is allowed on a certain resource. + * The rule implementation must be prepared to deal with resources for which it does + * not apply. In those cases it should return {@link AuthorizationResult#UNSUPPORTED_RESOURCE}. + * @param aResource Resource. + * @param anOperation Operation. + * @param aUser Current user. + * @return Authorization result. + */ + AuthorizationResult isAllowed(Object aResource, Operation anOperation, User aUser); +} diff --git a/security/src/main/java/org/wamblee/security/authorization/AuthorizationService.java b/security/src/main/java/org/wamblee/security/authorization/AuthorizationService.java new file mode 100644 index 00000000..867242a5 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/AuthorizationService.java @@ -0,0 +1,67 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import org.wamblee.persistence.Persistent; + +/** + * Service to determine if access to a certain resource is allowed. + */ +public interface AuthorizationService extends Persistent { + + /** + * Checks whether an operation is allowed on a resource. + * @param aResource Resource. + * @param aOperation Operation. + * @return Checks whether the operation is allowed on a resource. + */ + boolean isAllowed(Object aResource, Operation aOperation); + + /** + * Same as {@link #isAllowed(Object, Operation)} but throws a + * RuntimeException in case access is not allowed. + * @param aResource Resource to check. + * @param aOperation Operation to perform. + * @return Resource that was checked. + */ + T check(T aResource, Operation aOperation); + + /** + * Gets the authorization rules. + * @return Rules. + */ + AuthorizationRule[] getRules(); + + /** + * Appends a new authorization rule to the end. + * @param aRule Rule to append. + */ + void appendRule(AuthorizationRule aRule); + + /** + * Removes a rule. + * @param aRule Index of the rule to remove. + */ + void removeRule(int aIndex); + + /** + * Inserts a rule. + * @param aIndex Index of the position of the rule after insertion. + * @param aRule Rule to insert. + */ + void insertRuleAfter(int aIndex, AuthorizationRule aRule); +} diff --git a/security/src/main/java/org/wamblee/security/authorization/CreateOperation.java b/security/src/main/java/org/wamblee/security/authorization/CreateOperation.java new file mode 100644 index 00000000..7020a68d --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/CreateOperation.java @@ -0,0 +1,34 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +/** + * Represents an operation to create something. + */ +public class CreateOperation extends AllOperation { + + private static final String OPERATION = "create"; + + /** + * Constructs the operation. + * + */ + public CreateOperation() { + super(OPERATION); + } + +} diff --git a/security/src/main/java/org/wamblee/security/authorization/DefaultAuthorizationService.java b/security/src/main/java/org/wamblee/security/authorization/DefaultAuthorizationService.java new file mode 100644 index 00000000..d455ade2 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/DefaultAuthorizationService.java @@ -0,0 +1,153 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import java.util.ArrayList; +import java.util.List; + +import org.wamblee.persistence.AbstractPersistent; +import org.wamblee.usermgt.User; +import org.wamblee.usermgt.UserAccessor; + +/** + * Default implementation of an authorization service. + * To determine whether access to a resource is allowed, the service consults a number + * of authorization rules in a fixed order. The first rule that gives a result GRANTED or + * DENIED determines the result of the evaluation. Rules that return any other result are + * ignoed. If none of the rules match, than access is denied. + */ +public class DefaultAuthorizationService extends AbstractPersistent implements AuthorizationService { + + /** + * List of ordered authorization rules. + */ + private List _rules; + + /** + * User accessor used to obtain the current user. + */ + private UserAccessor _userAccessor; + + /** + * Name for this instance of the authorization service. + */ + private String _name; + + /** + * Constructs the service. + * @param aAccessor User accessor. + * @param aName Name of this instance of the service. + */ + public DefaultAuthorizationService(UserAccessor aAccessor, String aName) { + _rules = new ArrayList(); + _userAccessor = aAccessor; + _name = aName; + } + + /** + * Constructs the authorization service. + */ + public DefaultAuthorizationService() { + _rules = new ArrayList(); + _userAccessor = null; + _name = null; + } + + /** + * Sets the user accessor. + * @param aUserAccessor User accessor. + */ + public void setUserAccessor(UserAccessor aUserAccessor) { + _userAccessor = aUserAccessor; + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.AuthorizationService#isAllowed(java.lang.Object, org.wamblee.security.authorization.Operation) + */ + public boolean isAllowed(Object aResource, Operation aOperation) { + User user = _userAccessor.getCurrentUser(); + for (AuthorizationRule rule: _rules) { + switch ( rule.isAllowed(aResource, aOperation, user)) { + case DENIED: { return false; } + case GRANTED: { return true; } + } + } + return false; + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.AuthorizationService#check(T, org.wamblee.security.authorization.Operation) + */ + public T check(T aResource, Operation aOperation) { + if ( !isAllowed(aResource, aOperation)) { + throw new AuthorizationException(aResource, aOperation); + } + return aResource; + } + + protected String getName() { + return _name; + } + + public void setName(String aName) { + _name = aName; + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.AuthorizationService#getRules() + */ + public AuthorizationRule[] getRules() { + return _rules.toArray(new AuthorizationRule[0]); + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.AuthorizationService#appendRule(org.wamblee.security.authorization.AuthorizationRule) + */ + public void appendRule(AuthorizationRule aRule) { + _rules.add(aRule); + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.AuthorizationService#insertRuleAfter(int, org.wamblee.security.authorization.AuthorizationRule) + */ + public void insertRuleAfter(int aIndex, AuthorizationRule aRule) { + _rules.add(aIndex, aRule); + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.AuthorizationService#removeRule(int) + */ + public void removeRule(int aIndex) { + _rules.remove(aIndex); + } + + /** + * For OR mapping. + * @return The rules. + */ + protected List getMappedRules() { + return _rules; + } + + /** + * For OR mapping. + * @param aRules The rules. + */ + protected void setMappedRules(List aRules) { + _rules = aRules; + } +} diff --git a/security/src/main/java/org/wamblee/security/authorization/DefaultOperationRegistry.java b/security/src/main/java/org/wamblee/security/authorization/DefaultOperationRegistry.java new file mode 100644 index 00000000..7a26fb70 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/DefaultOperationRegistry.java @@ -0,0 +1,86 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import java.util.ArrayList; +import java.util.Map; +import java.util.TreeMap; + +/** + * Operation registry implementation. + * This implementation ignores the distinction between different types of resources + * and simply assumes that every operation is applicable to every type of resource. + */ +public class DefaultOperationRegistry implements OperationRegistry { + + private Map _operations; + + public DefaultOperationRegistry(Operation[] aOperations) { + _operations = new TreeMap(); + for (Operation operation: aOperations) { + _operations.put(operation.getName(), operation); + } + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.OperationRegistry#getOperations(java.lang.Class) + */ + public Operation[] getOperations(Class aResourceClass) { + return _operations.values().toArray(new Operation[0]); + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.OperationRegistry#encode(org.wamblee.security.authorization.Operation[]) + */ + public String encode(Operation[] aOperations) { + StringBuffer buffer = new StringBuffer(); + for (Operation operation: aOperations) { + if ( buffer.length() > 0 ) { + buffer.append(','); + } + buffer.append(operation.getName()); + } + return buffer.toString(); + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.OperationRegistry#decode(java.lang.Class, java.lang.String) + */ + public Operation[] decode(Class aResourceClass, String aOperationsString) { + return decode(aOperationsString); + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.OperationRegistry#decode(java.lang.String) + */ + public Operation[] decode(String aOperationsString) { + if ( aOperationsString.length() == 0 ) { + return new Operation[0]; + } + String[] names = aOperationsString.split(","); + ArrayList result = new ArrayList(); + for (String name: names) { + Operation operation = _operations.get(name); + if ( operation == null ) { + throw new IllegalArgumentException("Unknown operation '" + name + "'"); + } + result.add(operation); + } + return result.toArray(new Operation[0]); + } + +} diff --git a/security/src/main/java/org/wamblee/security/authorization/DeleteOperation.java b/security/src/main/java/org/wamblee/security/authorization/DeleteOperation.java new file mode 100644 index 00000000..45a78aaa --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/DeleteOperation.java @@ -0,0 +1,33 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +/** + * Deletes the operation. + */ +public class DeleteOperation extends AllOperation { + + private static final String OPERATION = "delete"; + + /** + * Constructs the operation. + * + */ + public DeleteOperation() { + super(OPERATION); + } +} diff --git a/security/src/main/java/org/wamblee/security/authorization/GroupUserCondition.java b/security/src/main/java/org/wamblee/security/authorization/GroupUserCondition.java new file mode 100644 index 00000000..de85067b --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/GroupUserCondition.java @@ -0,0 +1,77 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import org.wamblee.persistence.AbstractPersistent; +import org.wamblee.usermgt.User; + +/** + * Checks if a user against a specific group. + */ +public class GroupUserCondition extends AbstractPersistent implements UserCondition { + + /** + * Group the user must be in. + */ + private String _group; + + /** + * Constructs the condition. + * @param aGroup Group the user must be in. + */ + public GroupUserCondition(String aGroup) { + _group = aGroup; + } + + /** + * For OR mapping. + * + */ + protected GroupUserCondition() { + _group = null; + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.UserCondition#matches(org.wamblee.usermgt.UserAccessor) + */ + public boolean matches(User aUser) { + return aUser.isInGroup(_group); + } + + /** + * @return Returns the _group. + */ + protected String getGroup() { + return _group; + } + + /** + * @param _group The _group to set. + */ + protected void setGroup(String aGroup) { + _group = aGroup; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "GroupUserCondition(group=" + _group + ")"; + } + +} diff --git a/security/src/main/java/org/wamblee/security/authorization/IsaOperationCondition.java b/security/src/main/java/org/wamblee/security/authorization/IsaOperationCondition.java new file mode 100644 index 00000000..e5a4d686 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/IsaOperationCondition.java @@ -0,0 +1,98 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import org.wamblee.persistence.AbstractPersistent; + +/** + * Determiens if an operation is a subclass of a specified operation. + */ +public class IsaOperationCondition extends AbstractPersistent implements + OperationCondition { + + /** + * Operation that the other operation must be a subclass of. + */ + private Class _operation; + + /** + * Constructs the condition. + * + * @param aOperation + * Operation that an operation must be an instance of. + */ + public IsaOperationCondition(Class aOperation) { + _operation = aOperation; + } + + /** + * For OR mapping. + * + */ + public IsaOperationCondition() { + _operation = null; + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.security.authorization.OperationCondition#matches(org.wamblee.security.authorization.Operation) + */ + public boolean matches(Operation aOperation) { + return _operation.isInstance(aOperation); + } + + /** + * Gets the operation as a string. For OR mapping only. + * + * @return Operation string. + */ + protected String getOperationString() { + if (_operation == null) { + return null; + } + return _operation.getName(); + } + + /** + * Sets the operation as a string. For OR mapping only. + * + * @param aOperation + * Operation string. + */ + protected void setOperationString(String aOperation) { + if (aOperation == null ) { + return; + } + try { + _operation = (Class)Class.forName(aOperation); + } catch (Exception e) { + throw new IllegalArgumentException("Unknown class '" + aOperation + "'"); + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "IsaOperationCondition(operation=" + _operation.getName() + ")"; + } + +} diff --git a/security/src/main/java/org/wamblee/security/authorization/Operation.java b/security/src/main/java/org/wamblee/security/authorization/Operation.java new file mode 100644 index 00000000..09514464 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/Operation.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +/** + * Represents an operation on a resource. + * An operation should contain no state to be persisted since only the name of the + * operation is persisted. + */ +public interface Operation { + + /** + * Gets the name of the operation. + * @return Operation. + */ + String getName(); +} diff --git a/security/src/main/java/org/wamblee/security/authorization/OperationCondition.java b/security/src/main/java/org/wamblee/security/authorization/OperationCondition.java new file mode 100644 index 00000000..64a8356e --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/OperationCondition.java @@ -0,0 +1,33 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import org.wamblee.persistence.Persistent; + +/** + * Checks if an operation matches a condition. + */ +public interface OperationCondition extends Persistent { + + + /** + * Determines if the operation matches. + * @param aOperation Operation. + * @return True iff the operation matches. + */ + boolean matches(Operation aOperation); +} diff --git a/security/src/main/java/org/wamblee/security/authorization/OperationRegistry.java b/security/src/main/java/org/wamblee/security/authorization/OperationRegistry.java new file mode 100644 index 00000000..ac6772b8 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/OperationRegistry.java @@ -0,0 +1,55 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + + +/** + * Utility to map between a list of operations and a string based + * on the names of the operations. + */ +public interface OperationRegistry { + + /** + * Gets the supported operations for a given resource class. + * @param aResourceClass Resource class. + * @return Supported operations for that class. + */ + Operation[] getOperations(Class aResourceClass); + + /** + * Converts a number of operations to a string. + * @param aOperations Operations to convert. + * @return String representation of the allowed operations. + */ + String encode(Operation[] aOperations); + + /** + * Converts an operations string to an array of operations. + * @param aResourceClass Resource class. + * @param aOperationsString Operations string as returned by {@link #encode(Operation[])}. + * @return Operations array. + */ + Operation[] decode(Class aResourceClass, String aOperationsString); + + /** + * Converts an operations string to an array of operations. + * @param aOperationsString Operations string as returned by {@link #encode(Operation[])}. + * @return Operations array. + */ + Operation[] decode(String aOperationsString); + +} diff --git a/security/src/main/java/org/wamblee/security/authorization/PathCondition.java b/security/src/main/java/org/wamblee/security/authorization/PathCondition.java new file mode 100644 index 00000000..0d6e6baa --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/PathCondition.java @@ -0,0 +1,32 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import org.wamblee.persistence.Persistent; + +/** + * Checks if a path satisfies a condition. + */ +public interface PathCondition extends Persistent { + + /** + * Checks if the path matches the condition. + * @param aPath Path to match. + * @return True iff the path matches. + */ + boolean matches(String aPath); +} diff --git a/security/src/main/java/org/wamblee/security/authorization/ReadOperation.java b/security/src/main/java/org/wamblee/security/authorization/ReadOperation.java new file mode 100644 index 00000000..3016f340 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/ReadOperation.java @@ -0,0 +1,33 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +/** + * Represents a read operation on a resource. + */ +public class ReadOperation extends AllOperation { + + private static final String OPERATION = "read"; + + /** + * Constructs the operation. + * + */ + public ReadOperation() { + super(OPERATION); + } +} diff --git a/security/src/main/java/org/wamblee/security/authorization/RegexpPathCondition.java b/security/src/main/java/org/wamblee/security/authorization/RegexpPathCondition.java new file mode 100644 index 00000000..4941f06c --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/RegexpPathCondition.java @@ -0,0 +1,75 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import org.wamblee.persistence.AbstractPersistent; + +/** + * Condition to check whether a path matches a given regula expression. + */ +public class RegexpPathCondition extends AbstractPersistent implements PathCondition { + + /** + * String the path must start with. + */ + private String _pattern; + + /** + * Constructs the condition. + * @param aPattern String the path must start with. + */ + public RegexpPathCondition(String aPattern) { + _pattern = aPattern; + } + + /** + * For OR mapping. + * + */ + protected RegexpPathCondition() { + _pattern = null; + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.PathCondition#matches(java.lang.String) + */ + public boolean matches(String aPath) { + return aPath.matches(_pattern); + } + + /** + * @return Returns the _path. + */ + protected String getPattern() { + return _pattern; + } + + /** + * @param aPattern The _path to set. + */ + protected void setPattern(String aPattern) { + _pattern = aPattern; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "RegexpCondition(pattern = '" + _pattern + "')"; + } +} diff --git a/security/src/main/java/org/wamblee/security/authorization/StartsWithPathCondition.java b/security/src/main/java/org/wamblee/security/authorization/StartsWithPathCondition.java new file mode 100644 index 00000000..3096430e --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/StartsWithPathCondition.java @@ -0,0 +1,48 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + + +/** + * Condition to check whether a path starts with a given string. + */ +public class StartsWithPathCondition extends RegexpPathCondition { + + /** + * Constructs the condition. + * @param aPath String the path must start with. + */ + public StartsWithPathCondition(String aPath) { + super( aPath + ".*"); + } + + /** + * For OR mapping. + * + */ + protected StartsWithPathCondition() { + super(); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "StartsWithPathCondition(pattern = '" + getPattern() + "')"; + } +} diff --git a/security/src/main/java/org/wamblee/security/authorization/UrlAuthorizationRule.java b/security/src/main/java/org/wamblee/security/authorization/UrlAuthorizationRule.java new file mode 100644 index 00000000..0d22d1ca --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/UrlAuthorizationRule.java @@ -0,0 +1,264 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import static org.wamblee.security.authorization.AuthorizationResult.DENIED; +import static org.wamblee.security.authorization.AuthorizationResult.GRANTED; +import static org.wamblee.security.authorization.AuthorizationResult.UNDECIDED; +import static org.wamblee.security.authorization.AuthorizationResult.UNSUPPORTED_RESOURCE; + +import org.apache.log4j.Logger; +import org.wamblee.persistence.AbstractPersistent; +import org.wamblee.usermgt.User; + +/** + * Utility base class for implementation of authentication rules based on the + *
    + *
  • The path of the resource. To obtain the path of a resource, subclasses + * must implement {@link #getResourcePath(Object)}. + * Whether a path is appropriate is determined by a + * {@link org.wamblee.security.authorization.PathCondition}. + *
  • + *
  • The user identity with which the resource is accessed. + * Whether a user is appropriate is determined by + * a {@link org.wamblee.security.authorization.UserCondition}. + *
  • + *
  • The operation that is requested. + * Whether the operation is appropriate is determined by a + * {@link org.wamblee.security.authorization.OperationCondition}. + *
  • + *
+ * + * In case all three conditions match, the condition returns the configured + * result passed at construction (GRANTED or DENIED). If the resource is not + * of the specified type, the result is UNSUPPORTED_RESOURCE, otherwise, the + * result is UNDECIDED. + */ +public abstract class UrlAuthorizationRule extends AbstractPersistent implements AuthorizationRule { + + private static final Logger LOGGER = Logger.getLogger(UrlAuthorizationRule.class); + + /** + * Result that the rule will return in case there is a match. + */ + private AuthorizationResult _result; + + /** + * A condition which specifies which users the rule is for. + */ + private UserCondition _userCondition; + + /** + * Path the rule applies for. + */ + private PathCondition _pathCondition; + + /** + * Resource class that the rule applies for. + */ + private Class _resourceClass; + + /** + * Operation that this rule is for. + */ + private OperationCondition _operationCondition; + + /** + * Constructs an authorization rule. + * IF the group and path match, then the provided result will be returned. + * @param aResult Result of the authorization when the path and group match. + * @param aUserCondition Condition to match users. + * @param aPathCondition Condition to match paths with. + * @param aResourceClass Supported resource class this is for. + * @param aOperationCondition Condition to match the operation with. + */ + protected UrlAuthorizationRule(AuthorizationResult aResult, UserCondition aUserCondition, + PathCondition aPathCondition, Class aResourceClass, OperationCondition aOperationCondition) { + if ( !aResult.equals(GRANTED) && !aResult.equals(DENIED)) { + throw new IllegalArgumentException("Only GRANTED or DENIED may be used: " + aResult); + } + _result = aResult; + _userCondition = aUserCondition; + _pathCondition = aPathCondition; + _resourceClass = aResourceClass; + _operationCondition = aOperationCondition; + } + + /** + * For OR mapping. + * + */ + protected UrlAuthorizationRule(Class aResourceClass) { + _result = null; + _userCondition = null; + _pathCondition = null; + _resourceClass = aResourceClass; + _operationCondition = null; + } + + /** + * For OR mapping. + * + */ + protected UrlAuthorizationRule() { + _result = null; + _userCondition = null; + _pathCondition = null; + _resourceClass = null; + _operationCondition = null; + } + + + /* + * (non-Javadoc) + * + * @see org.wamblee.security.authorization.AuthorizationRule#getSupportedTypes() + */ + public Class[] getSupportedTypes() { + return new Class[] { _resourceClass }; + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.security.authorization.AuthorizationRule#isAllowed(java.lang.Object, + * org.wamblee.security.authorization.Operation) + */ + public AuthorizationResult isAllowed(Object aResource, Operation anOperation, User aUser) { + if ( ! _resourceClass.isInstance(aResource)) { + return UNSUPPORTED_RESOURCE; + } + String path = getResourcePath(aResource); + return isAllowed(path, anOperation, aUser); + } + + /** + * Determines if the operation is allowed on the resource. + * @param aPath Path of the resource. + * @param aOperation Operation to be done. + * @param aUser Currently logged in user or null if no user is logged in. + * @return Authorization result, + */ + protected AuthorizationResult isAllowed(String aPath, Operation aOperation, User aUser) { + if ( ! _pathCondition.matches(aPath) ) { + return UNDECIDED; + } + if ( !_operationCondition.matches(aOperation) ) { + return UNDECIDED; + } + if ( !_userCondition.matches(aUser)) { + return UNDECIDED; + } + return _result; + } + + /** + * Gets the path of the resource. + * @param aResource Resource, guaranteed to be an instance of + * {@link #_resourceClass}. + * @return Path of the resource. + */ + protected abstract String getResourcePath(Object aResource); + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "UrlAUthorizationRule(result = " + _result + + ", pathCondition = " + _pathCondition + + ", userCondition = " + _userCondition + + ", resourceClass = " + _resourceClass + ")"; + } + + /** + * Gets the authorization result for OR mapping. + * @return Result. + */ + protected String getAuthorizationResultString() { + if ( _result == null ) { + return null; + } + return _result.toString(); + } + + /** + * Sets the authorization result, for OR mapping. + * @param aResult Result. + */ + protected void setAuthorizationResultString(String aResult) { + _result = AuthorizationResult.valueOf(aResult); + } + + protected String getResourceClassName() { + if ( _resourceClass == null ) { + return ""; + } + return _resourceClass.getName(); + } + + protected void setResourceClassName(String aResourceClass) { + try { + _resourceClass = Class.forName(aResourceClass); + } catch (ClassNotFoundException e) { + LOGGER.error("Cannot find resource class '" + aResourceClass + "'", e); + throw new IllegalArgumentException(e.getMessage(), e); + } + } + + /** + * @return Returns the _operationCondition. + */ + public OperationCondition getOperationCondition() { + return _operationCondition; + } + + /** + * @param aOperationCondition The _operationCondition to set. + */ + protected void setOperationCondition(OperationCondition aOperationCondition) { + _operationCondition = aOperationCondition; + } + + /** + * @return Returns the _pathCondition. + */ + public PathCondition getPathCondition() { + return _pathCondition; + } + + /** + * @param aPathCondition The _pathCondition to set. + */ + protected void setPathCondition(PathCondition aPathCondition) { + _pathCondition = aPathCondition; + } + + /** + * @return Returns the _userCondition. + */ + public UserCondition getUserCondition() { + return _userCondition; + } + + /** + * @param aUserCondition The _userCondition to set. + */ + protected void setUserCondition(UserCondition aUserCondition) { + _userCondition = aUserCondition; + } +} diff --git a/security/src/main/java/org/wamblee/security/authorization/UserCondition.java b/security/src/main/java/org/wamblee/security/authorization/UserCondition.java new file mode 100644 index 00000000..3a215ed5 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/UserCondition.java @@ -0,0 +1,33 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import org.wamblee.persistence.Persistent; +import org.wamblee.usermgt.User; + +/** + * Condition used to match a user against a specified set of users. + */ +public interface UserCondition extends Persistent { + + /** + * Determines if the condition matches. + * @param aUser user to check. + * @return True if the condition matches, false otherwise. + */ + boolean matches(User aUser); +} diff --git a/security/src/main/java/org/wamblee/security/authorization/WriteOperation.java b/security/src/main/java/org/wamblee/security/authorization/WriteOperation.java new file mode 100644 index 00000000..a56a059e --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/WriteOperation.java @@ -0,0 +1,34 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +/** + * Represents a write operation on a resource. + */ +public class WriteOperation extends AllOperation { + + private static final String OPERATION = "write"; + + /** + * Constructs the operation. + * + */ + public WriteOperation() { + super(OPERATION); + } + +} diff --git a/security/src/main/java/org/wamblee/security/authorization/hibernate/PersistentAuthorizationService.java b/security/src/main/java/org/wamblee/security/authorization/hibernate/PersistentAuthorizationService.java new file mode 100644 index 00000000..e54877a0 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/authorization/hibernate/PersistentAuthorizationService.java @@ -0,0 +1,213 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization.hibernate; + +import java.util.List; + +import org.springframework.orm.hibernate3.HibernateTemplate; +import org.wamblee.persistence.AbstractPersistent; +import org.wamblee.persistence.hibernate.HibernateSupport; +import org.wamblee.security.authorization.AuthorizationRule; +import org.wamblee.security.authorization.AuthorizationService; +import org.wamblee.security.authorization.DefaultAuthorizationService; +import org.wamblee.security.authorization.Operation; +import org.wamblee.usermgt.UserAccessor; + +/** + * Authorization service with persistent storage. + * This is a wrapper for {@link org.wamblee.security.authorization.DefaultAuthorizationService} + * which refreshes the state of the service at certain time intervals. + */ +public class PersistentAuthorizationService extends AbstractPersistent + implements AuthorizationService { + + /** + * Name of query to find the service by name. + */ + private static final String FIND_QUERY = "findAuthorizationServiceByName"; + + /** + * Name of the query parameter for the service name. + */ + private static final String NAME_PARAM = "name"; + + /** + * Authorization service to use. + */ + private DefaultAuthorizationService _service; + + /** + * Hibernate template to use. + */ + private HibernateTemplate _template; + + /** + * User accessor. + */ + private UserAccessor _userAccessor; + + /** + * Name of the service. + */ + private String _name; + + /** + * Refresh interval in milliseconds. + */ + private final long _refreshInterval; + + /** + * Last refresh time. + */ + private long _lastRefreshTime; + + /** + * Constructs the persistent service. + * + * @param aName + * Name of the service. + * @param aTemplate + * Hibernate template for hibernate usage. + * @param aAccessor + * User accessor. + * @param aRefresh + * Whether or not to refresh the state of the service at the + * start of every operation. + */ + public PersistentAuthorizationService(String aName, + HibernateTemplate aTemplate, UserAccessor aAccessor, + long aRefreshInterval) { + _template = aTemplate; + _refreshInterval = aRefreshInterval; + _lastRefreshTime = System.currentTimeMillis(); + _userAccessor = aAccessor; + _name = aName; + } + + /** + * Initialize service if needed. + */ + private void initialize() { + if (_service == null) { + List result = _template + .findByNamedQueryAndNamedParam(FIND_QUERY, NAME_PARAM, + _name); + + if (result.size() > 1) { + throw new IllegalArgumentException( + "Returned more than one service for name '" + _name + + "' (" + result.size() + ")"); + } + + if (result.size() == 0) { + _service = new DefaultAuthorizationService(_userAccessor, _name); + _template.persist(_service); + } else { + _service = result.get(0); + _service.setUserAccessor(_userAccessor); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.security.authorization.AuthorizationService#isAllowed(java.lang.Object, + * org.wamblee.security.authorization.Operation) + */ + public boolean isAllowed(Object aResource, Operation aOperation) { + initialize(); + refresh(); + return _service.isAllowed(aResource, aOperation); + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.AuthorizationService#check(T, org.wamblee.security.authorization.Operation) + */ + public T check(T aResource, Operation aOperation) { + initialize(); + refresh(); + return _service.check(aResource, aOperation); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.security.authorization.AuthorizationService#getRules() + */ + public AuthorizationRule[] getRules() { + initialize(); + refresh(); + return _service.getRules(); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.security.authorization.AuthorizationService#appendRule(org.wamblee.security.authorization.AuthorizationRule) + */ + public void appendRule(AuthorizationRule aRule) { + initialize(); + refresh(); + _service.appendRule(aRule); + save(); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.security.authorization.AuthorizationService#removeRule(int) + */ + public void removeRule(int aIndex) { + initialize(); + refresh(); + _service.removeRule(aIndex); + save(); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.security.authorization.AuthorizationService#insertRuleAfter(int, + * org.wamblee.security.authorization.AuthorizationRule) + */ + public void insertRuleAfter(int aIndex, AuthorizationRule aRule) { + initialize(); + refresh(); + _service.insertRuleAfter(aIndex, aRule); + save(); + } + + /** + * Refreshes the state of the service through hibernate. + * + */ + private synchronized void refresh() { + long time = System.currentTimeMillis(); + if ( time - _lastRefreshTime > _refreshInterval ) { + _template.refresh(_service); + _lastRefreshTime = time; + } + } + + /** + * Saves any changes to the service state if necessary. + */ + private void save() { + HibernateSupport.merge(_template, _service); + } +} diff --git a/security/src/main/java/org/wamblee/security/encryption/Md5HexMessageDigester.java b/security/src/main/java/org/wamblee/security/encryption/Md5HexMessageDigester.java new file mode 100644 index 00000000..f6d970e0 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/encryption/Md5HexMessageDigester.java @@ -0,0 +1,53 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.encryption; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.apache.commons.codec.binary.Hex; + +/** + * MD5 Hex encoder. + */ +public class Md5HexMessageDigester implements MessageDigester { + + /** + * Constructs the message digester. + * + */ + public Md5HexMessageDigester() { + // Empty + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.security.MessageDigester#hash(java.lang.String) + */ + public String hash(String aValue) { + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); + byte[] result = digest.digest(aValue.getBytes()); + char[] charResult = new Hex().encodeHex(result); + return new String(charResult); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException("MD5 not supported????"); + } + } + +} diff --git a/security/src/main/java/org/wamblee/security/encryption/MessageDigester.java b/security/src/main/java/org/wamblee/security/encryption/MessageDigester.java new file mode 100644 index 00000000..685a4c79 --- /dev/null +++ b/security/src/main/java/org/wamblee/security/encryption/MessageDigester.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.encryption; + +/** + * Utility class that encapsulates a message digest method. + */ +public interface MessageDigester { + + /** + * Computes a message digest for a value and encodes it in some way. + * @param aValue Value to compute digest for. + * @return Encoded digest. + */ + String hash(String aValue); + +} diff --git a/security/src/main/java/org/wamblee/usermgt/AbstractUserSet.java b/security/src/main/java/org/wamblee/usermgt/AbstractUserSet.java new file mode 100644 index 00000000..1a3096f7 --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/AbstractUserSet.java @@ -0,0 +1,65 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import static org.wamblee.usermgt.UserMgtException.Reason.DUPLICATE_USER; + +import org.wamblee.security.encryption.MessageDigester; + +/** + * User set base class. + */ +public abstract class AbstractUserSet implements UserSet { + + /** + * Password validator. + */ + private NameValidator _passwordValidator; + + /** + * Password encoder. + */ + private MessageDigester _passwordEncoder; + + + protected AbstractUserSet(NameValidator aPasswordValidator, + MessageDigester aPasswordEncoder) { + _passwordValidator = aPasswordValidator; + _passwordEncoder = aPasswordEncoder; + } + + /** + * Sets the password validtor and encoder in the user. + * @param aUser User. + */ + protected void setPasswordInfo(User aUser) { + aUser.setPasswordValidator(_passwordValidator); + aUser.setPasswordEncoder(_passwordEncoder); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.UserSet#createUser(java.lang.String, java.lang.String, org.wamblee.usermgt.Group) + */ + public User createUser(String aUsername, String aPassword, Group aGroup) throws UserMgtException { + User user = new User(aUsername, aPassword, aGroup, _passwordValidator, _passwordEncoder); + if (contains(user)) { + throw new UserMgtException(DUPLICATE_USER, user); + } + add(user); + return user; + } +} diff --git a/security/src/main/java/org/wamblee/usermgt/Group.java b/security/src/main/java/org/wamblee/usermgt/Group.java new file mode 100644 index 00000000..d8f9f83c --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/Group.java @@ -0,0 +1,101 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.io.Serializable; + +import org.wamblee.persistence.AbstractPersistent; + +/** + * Represents a group. + */ +public class Group extends AbstractPersistent implements Serializable, Comparable { + + /** + * Group name. + */ + private String _name; + + /** + * Constructs the group. + * @param aName + */ + Group(String aName) { + super(); + _name = aName; + } + + public Group(Group aGroup) { + super(aGroup); + _name = aGroup._name; + } + + protected Group() { + super(); + _name = null; + } + + /** + * Gets the name of the group. + * @return Group name. + */ + public String getName() { + return _name; + } + + /** + * Sets the group name. + * @param aName Group name. + */ + void setName(String aName) { + _name = aName; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object aGroup) { + if ( !( aGroup instanceof Group )) { + return false; + } + return _name.equals(((Group)aGroup)._name); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return _name.hashCode(); + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(T) + */ + public int compareTo(Object aGroup) { + return _name.compareTo(((Group)aGroup)._name); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Group(pk = " + getPrimaryKey() + ", name=" + _name + ")"; + } +} diff --git a/security/src/main/java/org/wamblee/usermgt/GroupSet.java b/security/src/main/java/org/wamblee/usermgt/GroupSet.java new file mode 100644 index 00000000..e67d7bcf --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/GroupSet.java @@ -0,0 +1,71 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.util.Set; + +/** + * Represents a set of groups. A typical implemnetation would be, a readonly implementation + * defined in a configuration file or a list of groups defined in a database. + */ +public interface GroupSet { + + /** + * Must be called when the group has been modified to notify the group set. + * @param aGroup Group that was modified. + */ + void groupModified(Group aGroup); + + /** + * Finds the group by name. + * @param aName Group name. + * @return Group or null if not found. + */ + Group find(String aName); + + /** + * Determines if the group exists. + * @param aGroup Group. + * @return True iff the group exists. + */ + boolean contains(Group aGroup); + + /** + * Adds a group. If the group already exists, the existing group set + * is left unchanged. + * @param aGroup Group. + */ + boolean add(Group aGroup); + + /** + * Removes a group. If the group does not exist, this method is a no-op. + * @param aGroup Group to remove. + * @return True if the group was removed, false otherwise. + */ + boolean remove(Group aGroup); + + /** + * Returns the current groups. + * @return Groups. + */ + Set list(); + + /** + * @return The number of groups. + */ + int size(); +} diff --git a/security/src/main/java/org/wamblee/usermgt/InMemoryGroupSet.java b/security/src/main/java/org/wamblee/usermgt/InMemoryGroupSet.java new file mode 100644 index 00000000..04f8be22 --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/InMemoryGroupSet.java @@ -0,0 +1,98 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.util.Set; +import java.util.TreeSet; + +/** + * In-memory group set implementation. + */ +public class InMemoryGroupSet implements GroupSet { + + /** + * Groups. + */ + private Set _groups; + + /** + * Constructs an empty group set. + */ + public InMemoryGroupSet() { + _groups = new TreeSet(); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#groupModified(org.wamblee.usermgt.Group) + */ + public void groupModified(Group aGroup) { + _groups.remove(aGroup); + _groups.add(aGroup); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#find(java.lang.String) + */ + public Group find(String aName) { + for (Group group: _groups) { + if ( group.getName().equals(aName)) { + return new Group(group); + } + } + return null; + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#contains(org.wamblee.usermgt.Group) + */ + public boolean contains(Group aGroup) { + return _groups.contains(aGroup); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#add(org.wamblee.usermgt.Group) + */ + public boolean add(Group aGroup) { + return _groups.add(aGroup); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#remove(org.wamblee.usermgt.Group) + */ + public boolean remove(Group aGroup) { + return _groups.remove(aGroup); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#list() + */ + public Set list() { + Set list = new TreeSet(); + for (Group group: _groups) { + list.add(new Group(group)); + } + return list; + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#size() + */ + public int size() { + return _groups.size(); + } + +} diff --git a/security/src/main/java/org/wamblee/usermgt/InMemoryUserSet.java b/security/src/main/java/org/wamblee/usermgt/InMemoryUserSet.java new file mode 100644 index 00000000..0ccbba72 --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/InMemoryUserSet.java @@ -0,0 +1,130 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.util.Set; +import java.util.TreeSet; + +import org.wamblee.security.encryption.MessageDigester; + +/** + * In-memory user set. + */ +public class InMemoryUserSet extends AbstractUserSet { + + /** + * Users. All users in this set have their password validator and encoder set. + */ + private Set _users; + + /** + * Constructs an empty user set. + */ + public InMemoryUserSet(NameValidator aPasswordValidator, MessageDigester aPasswordEncoder) { + super(aPasswordValidator, aPasswordEncoder); + _users = new TreeSet(); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#userModified(org.wamblee.usermgt.User) + */ + public void userModified(User aUser) { + _users.remove(aUser); + setPasswordInfo(aUser); + _users.add(aUser); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#find(java.lang.String) + */ + public User find(String aName) { + for (User user : _users) { + if (user.getName().equals(aName)) { + return new User(user); + } + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#add(org.wamblee.usermgt.User) + */ + public boolean add(User aUser) { + setPasswordInfo(aUser); + return _users.add(aUser); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#contains(org.wamblee.usermgt.User) + */ + public boolean contains(User aUser) { + return _users.contains(aUser); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#remove(org.wamblee.usermgt.User) + */ + public boolean remove(User aUser) { + return _users.remove(aUser); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#list() + */ + public Set list() { + Set list = new TreeSet(); + for (User user : _users) { + list.add(new User(user)); + } + return list; + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#list(org.wamblee.usermgt.Group) + */ + public Set list(Group aGroup) { + Set result = new TreeSet(); + for (User user : _users) { + if (user.getGroups().contains(aGroup)) { + result.add(new User(user)); + } + } + return result; + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.UserSet#size() + */ + public int size() { + return _users.size(); + } + +} diff --git a/security/src/main/java/org/wamblee/usermgt/JaasUserAccessor.java b/security/src/main/java/org/wamblee/usermgt/JaasUserAccessor.java new file mode 100644 index 00000000..56ae836e --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/JaasUserAccessor.java @@ -0,0 +1,99 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.security.AccessController; +import java.security.Principal; +import java.util.Set; + +import javax.security.auth.Subject; + +/** + * Implementation of the user accessor that retrieves user information + * from JAAS. + */ +public class JaasUserAccessor implements UserAccessor { + + /** + * User administration to use. + */ + private UserAdministration _admin; + + /** + * Class of the JAAS user principal. + */ + private Class _userPrincipalClass; + + /** + * Constructs user accessor. + * @param aAdmin User administration. + * @param aUserClassName Class name of the user principal. + */ + public JaasUserAccessor(UserAdministration aAdmin, String aUserClassName) { + _admin = aAdmin; + try { + _userPrincipalClass = Class.forName(aUserClassName); + if ( !Principal.class.isAssignableFrom(_userPrincipalClass)) { + throw new IllegalArgumentException("Specified class '" + aUserClassName + "' is not a subclass of '" + + Principal.class.getName()); + } + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAccessor#getCurrentUser() + */ + public User getCurrentUser() { + Subject subject = Subject.getSubject(AccessController.getContext()); + if (subject == null) { + return null; + } + Principal userPrincipal = getUserPrincipal(subject); + + return _admin.getUser(userPrincipal.getName()); + } + + /** + * Gets the user principal from the subject. + * @param subject Subject. + * @return User principal. + * @throws IllegalArgumentException In case there is a duplicate principal or the principal was not found. + */ + private Principal getUserPrincipal(Subject subject) { + Set principals = subject.getPrincipals(); + Principal userPrincipal = null; + for ( Principal principal: principals) { + if ( principal.getClass().equals(_userPrincipalClass)) { + if ( userPrincipal != null ) { + throw new IllegalArgumentException( + "Multiple principals for class '" + _userPrincipalClass + "', subject: " + subject); + } + userPrincipal = principal; + } + } + if ( userPrincipal == null ) { + throw new IllegalArgumentException( + "No user principal found for class '" + _userPrincipalClass + "', subject: " + subject); + } + return userPrincipal; + } + +} diff --git a/security/src/main/java/org/wamblee/usermgt/NameValidator.java b/security/src/main/java/org/wamblee/usermgt/NameValidator.java new file mode 100644 index 00000000..cd066a0d --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/NameValidator.java @@ -0,0 +1,30 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +/** + * Validator of names. + */ +public interface NameValidator { + + /** + * Validates a name. + * @param aName Name + * @throws UserMgtException In case the name is invalid. + */ + void validate(String aName) throws UserMgtException; +} diff --git a/security/src/main/java/org/wamblee/usermgt/RegexpNameValidator.java b/security/src/main/java/org/wamblee/usermgt/RegexpNameValidator.java new file mode 100644 index 00000000..fdc220f2 --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/RegexpNameValidator.java @@ -0,0 +1,83 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import org.wamblee.usermgt.UserMgtException.Reason; + +/** + * Validation of names based on a regular expression. + */ +public class RegexpNameValidator implements NameValidator { + + /** + * Convenience pattern for an id. + */ + public static final String ID_PATTERN = "[a-zA-Z]+[a-zA-Z0-9]*"; + + /** + * Convenience pattern for a password consisting of at least 6 characters. + */ + public static final String PASSWORD_PATTERN = ".{6}.*"; + + /** + * Pattern to use. + */ + private String _pattern; + + /** + * Reason to use when validation fails. + */ + private Reason _reason; + + /** + * Message to report. + */ + private String _message; + + /** + * Validates a regular expression. + * @param aPattern Pattern that names must comply to. + * @param aReason Reason to report when validation fails. + * @param aMessage Message to report. + */ + public RegexpNameValidator(String aPattern, Reason aReason, String aMessage) { + _pattern = aPattern; + _reason = aReason; + _message = aMessage; + } + + /** + * Convenience constructor with all string parameters. Useful for configuration + * in Spring. + * @param aPattern Pattern to use. + * @param aReason Reason. + * @param aMessage Message. + */ + public RegexpNameValidator(String aPattern, String aReason, String aMessage) { + this(aPattern, Reason.valueOf(aReason), aMessage); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.NameValidator#validate(java.lang.String) + */ + public void validate(String aName) throws UserMgtException { + if ( !aName.matches(_pattern)) { + throw new UserMgtException(_reason, _message); + } + } + +} diff --git a/security/src/main/java/org/wamblee/usermgt/User.java b/security/src/main/java/org/wamblee/usermgt/User.java new file mode 100644 index 00000000..f239c141 --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/User.java @@ -0,0 +1,295 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.io.Serializable; +import java.util.Set; +import java.util.TreeSet; + +import org.wamblee.persistence.AbstractPersistent; +import org.wamblee.security.encryption.MessageDigester; +import org.wamblee.usermgt.UserMgtException.Reason; + +/** + * Represents a user. + * The methods for managing the groups of the user have package scope. + * Managing the groups of the user should be done through the + * {@link org.wamblee.usermgt.UserAdministration} interface. + */ +public class User extends AbstractPersistent implements Serializable, Comparable { + + /** + * User name. + */ + private String _name; + + /** + * Password. + */ + private String _password; + + /** + * Groups the user belongs to. + */ + private Set _groups; + + /** + * Password validator. + */ + private NameValidator _passwordValidator; + + /** + * Password encoder. + */ + private MessageDigester _passwordEncoder; + + /** + * Constructs the user. + * @param aName User name. + * @param aPassword Password. + * @param aGroup Group the user belongs to. + */ + User(String aName, String aPassword, Group aGroup, NameValidator aPasswordValidator, + MessageDigester aPasswordEncoder) throws UserMgtException { + super(); + _name = aName; + aPasswordValidator.validate(aPassword); + _password = aPasswordEncoder.hash(aPassword); + _groups = new TreeSet(); + _groups.add(aGroup); + _passwordValidator = aPasswordValidator; + _passwordEncoder = aPasswordEncoder; + } + + public User(User aUser) { + super(aUser); + _name = aUser._name; + _password = aUser._password; + _groups = new TreeSet(); + for (Group group: aUser._groups) { + _groups.add(new Group(group)); + } + _passwordValidator = aUser._passwordValidator; + _passwordEncoder = aUser._passwordEncoder; + } + + User() { + super(); + _name = null; + _password = null; + _groups = null; + _passwordValidator = null; + _passwordEncoder = null; + } + + /** + * Sets the password validator. + * @param aPasswordValidator Validator. + */ + public void setPasswordValidator(NameValidator aPasswordValidator) { + _passwordValidator = aPasswordValidator; + } + + /** + * Sets the password encoder. + * @param aPasswordEncoder Encoder. + */ + public void setPasswordEncoder(MessageDigester aPasswordEncoder) { + _passwordEncoder = aPasswordEncoder; + } + + /** + * @return Returns the _password. + */ + String getPassword() { + return _password; + } + + /** + * Checks the password. + * @param aPassword Password to check. + * @throws UserMgtException In case the password is incorrect. + */ + public void checkPassword(String aPassword) throws UserMgtException { + String encoded = _passwordEncoder.hash(aPassword); + if ( !_password.equals(encoded) ) { + throw new UserMgtException(Reason.INVALID_PASSWORD, this); + } + } + + /** + * Changes the password. + * @param aOldPassword Old password. + * @param aNewPassword New password. + * @throws UserMgtException In case the old password is incorrect. + */ + public void changePassword(String aOldPassword, String aNewPassword) throws UserMgtException { + checkPassword(aOldPassword); + _passwordValidator.validate(aNewPassword); + setPassword(aNewPassword); + } + + /** + * @param aPassword + * The password to set. + */ + public void setPassword(String aPassword) throws UserMgtException { + _passwordValidator.validate(aPassword); + _password = _passwordEncoder.hash(aPassword); + } + + /** + * For OR mapping. + * @return Password. + */ + protected String getPasswordString() { + return _password; + } + + /** + * For OR mapping. + * @param aPassword Password. + */ + protected void setPasswordString(String aPassword) { + _password = aPassword; + } + + /** + * @return Returns the _user. + */ + public String getName() { + return _name; + } + + /** + * @param aName + * The username to set. + */ + void setName(String aName) { + _name = aName; + } + + /** + * Gets the groups the user belongs to. + * @return Groups. + */ + public Set getGroups() { + Set result = new TreeSet(); + result.addAll(_groups); + return result; + } + + /** + * Checks whether the user belongs to the given group. + * @param aGroup Group. + * @return True if the user belongs to the group. + */ + public boolean isInGroup(Group aGroup) { + return _groups.contains(aGroup); + } + + /** + * Checks whether the user belongs to the given group. + * @param aGroup Group. + * @return True if the user belongs to the group. + */ + public boolean isInGroup(String aGroup) { + return _groups.contains(new Group(aGroup)); + } + + /** + * Gets the group set. For OR mapping. + * @return set of groups. + */ + Set getGroupSet() { + return _groups; + } + + /** + * Sets the groups the user belongs to, for OR mapping. + * @param aGroups Groups. + */ + void setGroupSet(Set aGroups) { + _groups = aGroups; + } + + /** + * Adds the user to a group. + * @param aGroup Group to add the user to. + * @throws UserMgtException In case the user already belongs to the group. + */ + void addGroup(Group aGroup) throws UserMgtException { + if (_groups.contains(aGroup)) { + throw new UserMgtException(Reason.USER_ALREADY_IN_GROUP, aGroup); + } + _groups.add(aGroup); + } + + /** + * Removes the user from a group. + * @param aGroup Group. + * @throws UserMgtException In case the user does not belong to the group. + */ + void removeGroup(Group aGroup) throws UserMgtException { + if (!_groups.contains(aGroup)) { + throw new UserMgtException(Reason.USER_NOT_IN_GROUP, this, aGroup); + } + if ( _groups.size() == 1 ) { + throw new UserMgtException(Reason.USER_MUST_BE_IN_A_GROUP, this, aGroup); + } + _groups.remove(aGroup); + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object aUser) { + if ( !(aUser instanceof User)) { + return false; + } + User user = (User)aUser; + return _name.equals(user._name); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return _name.hashCode(); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + String result = "User(name=" + _name + ", password=" + _password; + for (Group group: _groups) { + result += ", group=" + group; + } + return result + ")"; + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(T) + */ + public int compareTo(Object aUser) { + return _name.compareTo(((User)aUser)._name); + } +} diff --git a/security/src/main/java/org/wamblee/usermgt/UserAccessor.java b/security/src/main/java/org/wamblee/usermgt/UserAccessor.java new file mode 100644 index 00000000..d36b6d31 --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/UserAccessor.java @@ -0,0 +1,28 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +/** + * Interface for accessing the currently logged in user. + */ +public interface UserAccessor { + /** + * Gets the current user. + * @return Currently logged in user or null if no user is found. + */ + User getCurrentUser(); +} diff --git a/security/src/main/java/org/wamblee/usermgt/UserAdminInitializer.java b/security/src/main/java/org/wamblee/usermgt/UserAdminInitializer.java new file mode 100644 index 00000000..dee34541 --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/UserAdminInitializer.java @@ -0,0 +1,81 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.security.NoSuchAlgorithmException; + +import org.apache.log4j.Logger; + +/** + * User administration initializer. It populates the user administration with a + * number of groups and users but only in case no users exist. + */ +public class UserAdminInitializer { + + private static final Logger LOGGER = Logger.getLogger(UserAdminInitializer.class); + + /** + * Initializes the user administration in case no users are present. + * + */ + public UserAdminInitializer(UserAdministration aAdmin, String[] aUsers, + String[] aGroups, String[] aPasswords) throws UserMgtException, NoSuchAlgorithmException { + if (aUsers.length != aGroups.length + || aUsers.length != aPasswords.length) { + throw new IllegalArgumentException( + "Array sizes for users, groups, and passwords differ: " + + aUsers.length + "," + aGroups.length + "," + + aPasswords.length); + + } + if (aAdmin.getUserCount() == 0) { + initialize(aAdmin, aUsers, aGroups, aPasswords); + } + + } + + /** + * Adds the specified users and groups to the user administration. + * @param aAdmin User administration. + * @param aUsers Users. + * @param aGroups Groups. + * @param aPasswords Passwords. + * @throws UserMgtException In case of a problem creating users or groups. + */ + private void initialize(UserAdministration aAdmin, String[] aUsers, + String[] aGroups, String[] aPasswords) throws UserMgtException { + for (int i = 0; i < aUsers.length; i++) { + String user = aUsers[i]; + String group = aGroups[i]; + String password = aPasswords[i]; + + if (aAdmin.getUser(user) == null) { + // must create user. + Group groupObj = aAdmin.getGroup(group); + if (groupObj == null) { + // must create group + LOGGER.info("Creating group: " + group); + groupObj = aAdmin.createGroup(group); + } + assert groupObj != null; + + LOGGER.info("Creating user: " + user + " password: " + password); + aAdmin.createUser(user, password, groupObj); + } + } + } +} diff --git a/security/src/main/java/org/wamblee/usermgt/UserAdministration.java b/security/src/main/java/org/wamblee/usermgt/UserAdministration.java new file mode 100644 index 00000000..74d2dffc --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/UserAdministration.java @@ -0,0 +1,149 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.util.Set; + +/** + * Interface for user administration. Manages the users and groups. + */ +public interface UserAdministration { + + /** + * Creates a new user. + * @param aUser Username. + * @param aPassword Password. + * @param aGroup Group. + * @return User. + * @throws UserMgtException In case there is a conflict with an existing user. + */ + User createUser(String aUser, String aPassword, Group aGroup) throws UserMgtException; + + /** + * Creates a new group. + * @param aName Group name. + * @return Group + * @throws UserMgtException In case there is a conflict with an existing group. + */ + Group createGroup(String aName) throws UserMgtException; + + /** + * @return Number of users. + */ + int getUserCount(); + + /** + * @return Number of groups. + */ + int getGroupCount(); + + /** + * Must be called when the user is modified. + * @param aUser User. + */ + void userModified(User aUser); + + /** + * Must be called when the group is modified. + * @param aGroup Group. + */ + void groupModified(Group aGroup); + + /** + * Gets the user for a given name. + * @param aName User name. + * @return User or null if not found. + */ + User getUser(String aName); + + /** + * Gets the group for a given group name. + * @param aName Group name. + * @return Group or null if not found. + */ + Group getGroup(String aName); + + /** + * Get the users. + * @return All known users. + */ + Set getUsers(); + + /** + * Gets the users for a given group. + * @param aGroup Group. + * @return Set of users (always non-null). + */ + Set getUsers(Group aGroup); + + /** + * Gets all known groups. + * @return Groups. + */ + Set getGroups(); + + /** + * Renames a user. + * @param aUser User object for which user name must be changed. + * @param aUserName New user name. + * @throws UserMgtException In case the user is not known or the new user + * name is already in use by another user. + */ + void renameUser(User aUser, String aUserName) throws UserMgtException; + + /** + * Renames a group. + * @param aGroup Group to rename. + * @param aGroupName New name for the group. + * @throws UserMgtException In case the new group name is already used by + * another group of if the existing group is unknown. + */ + void renameGroup(Group aGroup, String aGroupName) throws UserMgtException; + + /** + * Removes the user. + * @param aUser User to remove. + * @throws UserMgtException In case the user does not exist. + */ + void removeUser(User aUser) throws UserMgtException; + + /** + * Removes the group. + * @param aGroup Group to remove. + * @throws UserMgtException In case there are still users that are in the given group. + */ + void removeGroup(Group aGroup) throws UserMgtException; + + /** + * Adds a user to a group. + * @param aUser User. + * @param aGroup Group. + * @throws UserMgtException In case the user or group or not known or if the user + * is already part of the group. + */ + void addUserToGroup(User aUser, Group aGroup) throws UserMgtException; + + /** + * Removes a user from a group. + * @param aUser User + * @param aGroup Group + * @throws UserMgtException In case the user or group are unknown or if the user + * is not part of the group. + */ + void removeUserFromGroup(User aUser, Group aGroup) throws UserMgtException; +} + diff --git a/security/src/main/java/org/wamblee/usermgt/UserAdministrationImpl.java b/security/src/main/java/org/wamblee/usermgt/UserAdministrationImpl.java new file mode 100644 index 00000000..a29d6d40 --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/UserAdministrationImpl.java @@ -0,0 +1,287 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import static org.wamblee.usermgt.UserMgtException.Reason.DUPLICATE_GROUP; +import static org.wamblee.usermgt.UserMgtException.Reason.DUPLICATE_USER; +import static org.wamblee.usermgt.UserMgtException.Reason.GROUP_STILL_OCCUPIED; +import static org.wamblee.usermgt.UserMgtException.Reason.TRIVIAL_RENAME; +import static org.wamblee.usermgt.UserMgtException.Reason.UNKNOWN_GROUP; +import static org.wamblee.usermgt.UserMgtException.Reason.UNKNOWN_USER; + +import java.util.Set; + +/** + * Administration of users and groups. + */ +public class UserAdministrationImpl implements UserAdministration { + + /** + * All known users. + */ + private UserSet _users; + + /** + * All known groups. + */ + private GroupSet _groups; + + /** + * Validator for user names. + */ + private NameValidator _userValidator; + + /** + * Validator for group names. + */ + private NameValidator _groupValidator; + + /** + * Constructs empty user administration. + * + */ + public UserAdministrationImpl(UserSet aUsers, GroupSet aGroups, NameValidator aUserValidator, + NameValidator aGroupValidator) { + _users = aUsers; + _groups = aGroups; + _userValidator = aUserValidator; + _groupValidator = aGroupValidator; + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#createUser(java.lang.String, + * java.lang.String) + */ + public User createUser(String aUser, String aPassword, Group aGroup) + throws UserMgtException { + _userValidator.validate(aUser); + checkGroup(aGroup); + User user = _users.createUser(aUser, aPassword, aGroup); + return new User(user); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#createGroup(java.lang.String) + */ + public Group createGroup(String aName) throws UserMgtException { + _groupValidator.validate(aName); + Group group = new Group(aName); + if (_groups.contains(group)) { + throw new UserMgtException(DUPLICATE_GROUP, group); + } + _groups.add(group); + return new Group(group); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#userModified(org.wamblee.usermgt.User) + */ + public void userModified(User aUser) { + _users.userModified(aUser); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#groupModified(org.wamblee.usermgt.Group) + */ + public void groupModified(Group aGroup) { + _groups.groupModified(aGroup); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#getUser(java.lang.String) + */ + public User getUser(String aName) { + return _users.find(aName); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#getGroup(java.lang.String) + */ + public Group getGroup(String aName) { + return _groups.find(aName); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#getUsers() + */ + public Set getUsers() { + return _users.list(); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#getUsers(org.wamblee.usermgt.Group) + */ + public Set getUsers(Group aGroup) { + return _users.list(aGroup); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#getGroups() + */ + public Set getGroups() { + return _groups.list(); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#removeUser(org.wamblee.usermgt.User) + */ + public void removeUser(User aUser) throws UserMgtException { + checkUser(aUser); + _users.remove(aUser); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#removeGroup(org.wamblee.usermgt.Group) + */ + public void removeGroup(Group aGroup) throws UserMgtException { + checkGroup(aGroup); + if (getUsers(aGroup).size() > 0) { + throw new UserMgtException(GROUP_STILL_OCCUPIED, aGroup); + } + _groups.remove(aGroup); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#renameUser(org.wamblee.usermgt.User, + * java.lang.String) + */ + public void renameUser(User aUser, String aUserName) + throws UserMgtException { + checkUser(aUser); + if (aUser.getName().equals(aUserName)) { + throw new UserMgtException(TRIVIAL_RENAME, aUser); + } + if (_users.find(aUserName) != null) { + throw new UserMgtException(DUPLICATE_USER, aUser); + } + _userValidator.validate(aUserName); + // we are modifying the user so we should re-insert it into the set + // after renaming it. + _users.remove(aUser); + aUser.setName(aUserName); + _users.add(aUser); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#renameGroup(org.wamblee.usermgt.Group, + * java.lang.String) + */ + public void renameGroup(Group aGroup, String aGroupName) + throws UserMgtException { + checkGroup(aGroup); + if (aGroup.getName().equals(aGroupName)) { + throw new UserMgtException(TRIVIAL_RENAME, aGroup); + } + if (_groups.find(aGroupName) != null) { + throw new UserMgtException(DUPLICATE_GROUP, aGroup); + } + _groupValidator.validate(aGroupName); + // we are renaming the group so we should re-insert it into the set + // after renaming it. + _groups.remove(aGroup); + aGroup.setName(aGroupName); + _groups.add(aGroup); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#addUserToGroup(org.wamblee.usermgt.User, + * org.wamblee.usermgt.Group) + */ + public void addUserToGroup(User aUser, Group aGroup) + throws UserMgtException { + checkUser(aUser); + checkGroup(aGroup); + aUser.addGroup(aGroup); + _users.userModified(aUser); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministration#removeUserFromGroup(org.wamblee.usermgt.User, + * org.wamblee.usermgt.Group) + */ + public void removeUserFromGroup(User aUser, Group aGroup) + throws UserMgtException { + checkUser(aUser); + checkGroup(aGroup); + aUser.removeGroup(aGroup); + _users.userModified(aUser); + } + + /** + * @param aUser + * @throws UserMgtException + */ + private void checkUser(User aUser) throws UserMgtException { + if (!_users.contains(aUser)) { + throw new UserMgtException(UNKNOWN_USER, aUser); + } + } + + /** + * @param aGroup + * @throws UserMgtException + */ + private void checkGroup(Group aGroup) throws UserMgtException { + if (!_groups.contains(aGroup)) { + throw new UserMgtException(UNKNOWN_GROUP, aGroup); + } + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.UserAdministration#getUserCount() + */ + public int getUserCount() { + return _users.size(); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.UserAdministration#getGroupCount() + */ + public int getGroupCount() { + return _groups.size(); + } +} diff --git a/security/src/main/java/org/wamblee/usermgt/UserMgtException.java b/security/src/main/java/org/wamblee/usermgt/UserMgtException.java new file mode 100644 index 00000000..4a95caa4 --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/UserMgtException.java @@ -0,0 +1,126 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.util.EnumMap; + +/** + * User management exception. + */ +public class UserMgtException extends Exception { + + static final long serialVersionUID = 5585349754997507529L; + + /** + * Possible causes for the exception. + * + */ + public enum Reason { + UNKNOWN_USER, + UNKNOWN_GROUP, + DUPLICATE_USER, + DUPLICATE_GROUP, + USER_ALREADY_IN_GROUP, + USER_NOT_IN_GROUP, + TRIVIAL_RENAME, + INVALID_PASSWORD, + GROUP_STILL_OCCUPIED, + USER_MUST_BE_IN_A_GROUP, + INVALID_USERNAME, + INVALID_GROUPNAME + } + + /** + * Mapping of enum to exception message text. + */ + private static final EnumMap MESSAGES = new EnumMap(Reason.class); + + static { + MESSAGES.put(Reason.UNKNOWN_USER, "Unknown user"); + MESSAGES.put(Reason.UNKNOWN_GROUP, "Unknown group"); + MESSAGES.put(Reason.DUPLICATE_USER, "Duplicate user"); + MESSAGES.put(Reason.DUPLICATE_GROUP, "Duplicate group"); + MESSAGES.put(Reason.USER_ALREADY_IN_GROUP, "User already in group"); + MESSAGES.put(Reason.USER_NOT_IN_GROUP, "User not in group"); + MESSAGES.put(Reason.TRIVIAL_RENAME, "Trivial rename"); + MESSAGES.put(Reason.INVALID_PASSWORD, "Invalid password"); + MESSAGES.put(Reason.GROUP_STILL_OCCUPIED, "Group still occupied"); + MESSAGES.put(Reason.USER_MUST_BE_IN_A_GROUP, "User must be in at least one group"); + MESSAGES.put(Reason.INVALID_USERNAME, "Invalid user name"); + MESSAGES.put(Reason.INVALID_GROUPNAME, "Invalid group name"); + } + + /** + * Cause of the exception. + */ + private Reason _cause; + + /** + * User or null if no user is relevant for the problem. + */ + private User _user; + + /** + * Group or null if no group is relevant for the problem. + */ + private Group _group; + + public UserMgtException(Reason aCause, String aMessage) { + super(MESSAGES.get(aCause) + ": " + aMessage); + _cause = aCause; + } + + public UserMgtException(Reason aCause, User aUser) { + this(aCause, "for user '" + aUser.getName() + "'"); + _user = aUser; + } + + public UserMgtException(Reason aCause, Group aGroup) { + this(aCause, "for group '" + aGroup.getName() + "'"); + _group = aGroup; + } + + public UserMgtException(Reason aCause, User aUser, Group aGroup) { + this(aCause, "for user '" + aUser.getName() + "' and group '" + aGroup.getName() + "'"); + _user = aUser; + _group = aGroup; + } + + /** + * Gets the cause of the problem. + * @return Cause. + */ + public Reason getReason() { + return _cause; + } + + /** + * Gets the user for which the problem occurred. + * @return User or null if not applicable. + */ + public User getUser() { + return _user; + } + + /** + * Gets the group for which the problem occured. + * @return Group or null if not applicable. + */ + public Group getGroup() { + return _group; + } +} diff --git a/security/src/main/java/org/wamblee/usermgt/UserSet.java b/security/src/main/java/org/wamblee/usermgt/UserSet.java new file mode 100644 index 00000000..ffff4abd --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/UserSet.java @@ -0,0 +1,90 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.util.Set; + +/** + * Represents a set of users. + * Typical implementations would be an implementation based on a static configuration file or + * an implementation backed by a database. + */ +public interface UserSet { + + /** + * Creates a user. + * @param aUsername User name. + * @param aPassword Password. + * @param aGroup Group. + * @return New user. + * @throws UserMgtException In case the user cannot be created. + */ + User createUser(String aUsername, String aPassword, Group aGroup) throws UserMgtException; + + /** + * Must be called whenever a user object has been modified to notify the + * user set. + * @param aUser Modified user. + */ + void userModified(User aUser); + + /** + * Finds user. + * @param aName Username. + * @return User or null if not found. + */ + User find(String aName); + + /** + * Checks if a user exists. + * @param aUser User. + * @return True iff the user exists. + */ + boolean contains(User aUser); + + /** + * Adds a user. If the user already exists, the user details are updated with that + * of the specified user object. + * @param aUser User to add. + */ + boolean add(User aUser); + + /** + * Removes a user. If the user does not exist, nothing happens. + * @param aUser + */ + boolean remove(User aUser); + + /** + * Lists the current users. + * @return Users. + */ + Set list(); + + /** + * Lists the users belonging to a particular group. + * @param aGroup Group. + * @return Groups. + */ + Set list(Group aGroup); + + /** + * + * @return The number of users. + */ + int size(); +} diff --git a/security/src/main/java/org/wamblee/usermgt/hibernate/HibernateGroupSet.java b/security/src/main/java/org/wamblee/usermgt/hibernate/HibernateGroupSet.java new file mode 100644 index 00000000..5e3de652 --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/hibernate/HibernateGroupSet.java @@ -0,0 +1,118 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt.hibernate; + +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.wamblee.persistence.hibernate.HibernateSupport; +import org.wamblee.usermgt.Group; +import org.wamblee.usermgt.GroupSet; + +/** + * Set of groups backed by the database. + */ +public class HibernateGroupSet extends HibernateSupport implements GroupSet { + + + private static final String QUERY_FIND_BY_NAME = "findGroupByName"; + + private static final String PARAM_NAME = "name"; + + private static final String QUERY_COUNT_GROUPS = "countGroups"; + + public HibernateGroupSet() { + // Empty + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#groupModified(org.wamblee.usermgt.Group) + */ + public void groupModified(Group aGroup) { + assert aGroup.getPrimaryKey() != null; + super.merge(aGroup); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#find(java.lang.String) + */ + public Group find(String aName) { + List list = getHibernateTemplate().findByNamedQueryAndNamedParam(QUERY_FIND_BY_NAME, PARAM_NAME, aName); + if ( list.size() > 1 ) { + throw new RuntimeException("More than one group with the same name '" + aName + "'"); + } + if ( list.size() == 0 ) { + return null; + } + return new Group((Group)list.get(0)); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#contains(org.wamblee.usermgt.Group) + */ + public boolean contains(Group aGroup) { + return find(aGroup.getName()) != null; + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#add(org.wamblee.usermgt.Group) + */ + public boolean add(Group aGroup) { + assert aGroup.getPrimaryKey() == null; + if ( contains(aGroup) ) { + return false; + } + super.merge(aGroup); + return true; + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#remove(org.wamblee.usermgt.Group) + */ + public boolean remove(Group aGroup) { + assert aGroup.getPrimaryKey() != null; + if ( !contains(aGroup)) { + return false; + } + Group group = (Group) getHibernateTemplate().merge(aGroup); + getHibernateTemplate().delete(group); + aGroup.setPrimaryKey(null); + aGroup.setPersistedVersion(-1); + return true; + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#list() + */ + public Set list() { + Set groups = new TreeSet(); + List list = getHibernateTemplate().loadAll(Group.class); + for (Group group: list) { + groups.add(new Group(group)); + } + return groups; + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.GroupSet#size() + */ + public int size() { + Long result = (Long) getHibernateTemplate().findByNamedQuery(QUERY_COUNT_GROUPS).get(0); + return result.intValue(); + } +} diff --git a/security/src/main/java/org/wamblee/usermgt/hibernate/HibernateUserSet.java b/security/src/main/java/org/wamblee/usermgt/hibernate/HibernateUserSet.java new file mode 100644 index 00000000..1c9237dd --- /dev/null +++ b/security/src/main/java/org/wamblee/usermgt/hibernate/HibernateUserSet.java @@ -0,0 +1,202 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt.hibernate; + +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.hibernate.SessionFactory; +import org.springframework.orm.hibernate3.HibernateTemplate; +import org.wamblee.cache.Cache; +import org.wamblee.persistence.hibernate.HibernateSupport; +import org.wamblee.security.encryption.MessageDigester; +import org.wamblee.usermgt.AbstractUserSet; +import org.wamblee.usermgt.Group; +import org.wamblee.usermgt.NameValidator; +import org.wamblee.usermgt.User; + +/** + * User set backed by the database. + */ +public class HibernateUserSet extends AbstractUserSet { + + private static final String QUERY_FIND_BY_NAME = "findUserByName"; + + private static final String QUERY_FIND_BY_GROUP_NAME = "findUserByGroupName"; + + private static final String PARAM_NAME = "name"; + + private static final String QUERY_COUNT_USERS = "countUsers"; + + /** + * Cache of users. Every user in the cache has its password validator and encoder set. + */ + private Cache _cache; + + /** + * Spring hibernate support. + */ + private HibernateSupport _hibernateSupport; + + /** + * Constructs a user set backed by the database. + * @param aCache User cache to use. + */ + public HibernateUserSet(Cache aCache, + NameValidator aPasswordValidator, MessageDigester aPasswordEncoder) { + super(aPasswordValidator, aPasswordEncoder); + _cache = aCache; + _hibernateSupport = new HibernateSupport(); + } + + /** + * Sets the session factory. + * @param aFactory Session factory. + */ + public void setSessionFactory(SessionFactory aFactory) { + _hibernateSupport.setSessionFactory(aFactory); + } + + /** + * Gets the hibernate template. + * @return Hibernate template. + */ + private HibernateTemplate getHibernateTemplate() { + return _hibernateSupport.getHibernateTemplate(); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#userModified(org.wamblee.usermgt.User) + */ + public void userModified(User aUser) { + assert aUser.getPrimaryKey() != null; + _hibernateSupport.merge(aUser); + _cache.remove(aUser.getName()); + setPasswordInfo(aUser); + _cache.put(aUser.getName(), new User(aUser)); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#find(java.lang.String) + */ + public User find(String aName) { + User user = _cache.get(aName); + if (user != null) { + return user; + } + List result = getHibernateTemplate().findByNamedQueryAndNamedParam( + QUERY_FIND_BY_NAME, PARAM_NAME, aName); + if (result.size() > 1) { + throw new RuntimeException( + "Implementation problem, more than one user with the same name!"); + } + if (result.size() == 0) { + return null; + } + user = (User) result.get(0); + setPasswordInfo(user); + _cache.put(aName, user); + return new User(user); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#contains(org.wamblee.usermgt.User) + */ + public boolean contains(User aUser) { + return find(aUser.getName()) != null; + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#add(org.wamblee.usermgt.User) + */ + public boolean add(User aUser) { + assert aUser.getPrimaryKey() == null; + if (contains(aUser)) { + return false; + } + getHibernateTemplate().saveOrUpdate(aUser); + setPasswordInfo(aUser); + _cache.put(aUser.getName(), aUser); + return true; + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#remove(org.wamblee.usermgt.User) + */ + public boolean remove(User aUser) { + assert aUser.getPrimaryKey() != null; + if (!contains(aUser)) { + return false; + } + User user = (User) getHibernateTemplate().merge(aUser); + getHibernateTemplate().delete(user); + aUser.setPersistedVersion(-1); + aUser.setPrimaryKey(null); + _cache.remove(aUser.getName()); + return true; + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#list() + */ + public Set list() { + Set users = new TreeSet(); + List list = getHibernateTemplate().loadAll(User.class); + for (User user : list) { + setPasswordInfo(user); + users.add(new User(user)); + } + return users; + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserSet#list(org.wamblee.usermgt.Group) + */ + public Set list(Group aGroup) { + Set users = new TreeSet(); + List list = getHibernateTemplate().findByNamedQueryAndNamedParam( + QUERY_FIND_BY_GROUP_NAME, PARAM_NAME, aGroup.getName()); + for (User user : list) { + setPasswordInfo(user); + users.add(new User(user)); + } + return users; + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.UserSet#size() + */ + public int size() { + Long result = (Long)getHibernateTemplate().findByNamedQuery(QUERY_COUNT_USERS).get(0); + return result.intValue(); + } +} diff --git a/security/src/test/java/org/wamblee/security/authorization/AuthorizationServiceTest.java b/security/src/test/java/org/wamblee/security/authorization/AuthorizationServiceTest.java new file mode 100644 index 00000000..f3791a34 --- /dev/null +++ b/security/src/test/java/org/wamblee/security/authorization/AuthorizationServiceTest.java @@ -0,0 +1,187 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import static org.wamblee.security.authorization.AuthorizationResult.DENIED; +import static org.wamblee.security.authorization.AuthorizationResult.GRANTED; + +import org.wamblee.persistence.hibernate.HibernateMappingFiles; +import org.wamblee.test.SpringConfigFiles; +import org.wamblee.test.SpringTestCase; +import org.wamblee.usermgt.UserAccessor; + +/** + * Tests the authorization service. + */ +public class AuthorizationServiceTest extends SpringTestCase { + + private AuthorizationRule _rule1; + private AuthorizationRule _rule2; + private AuthorizationRule _rule3; + private AuthorizationService _service; + + + public AuthorizationServiceTest() { + super(SpringConfigFiles.class, HibernateMappingFiles.class); + } + + public AuthorizationServiceTest(ClassaSpringFiles, + Class aMappings) { + super(aSpringFiles, aMappings); + } + + protected AuthorizationService getService() { + return _service; + } + + /* (non-Javadoc) + * @see junit.framework.TestCase#setUp() + */ + @Override + protected void setUp() throws Exception { + super.setUp(); + + _rule1 = createRule(GRANTED, "users", "/oni/", AllOperation.class); + _rule2 = createRule(DENIED, "users", "/abc/", ReadOperation.class); + _rule3 = createRule(GRANTED, "users", "/abc/", AllOperation.class); + + _service = createService(); + _service.appendRule(_rule1); + _service.appendRule(_rule2); + _service.appendRule(_rule3); + } + + protected void resetTestRules() { + ((TestAuthorizationRule)_rule1).reset(); + ((TestAuthorizationRule)_rule2).reset(); + ((TestAuthorizationRule)_rule3).reset(); + } + + protected UserAccessor createUserAccessor() { + return new TestUserAccessor(); + } + + /** + * Creates an authorization service with some rules for testing. . + * @return Authorization service. + */ + protected AuthorizationService createService() { + DefaultAuthorizationService service = new DefaultAuthorizationService() ; + service.setUserAccessor(createUserAccessor()); + return service; + } + + protected AuthorizationRule createRule(AuthorizationResult aResult, String aGroup, String aPath, Class aOperation) { + return new TestAuthorizationRule(aResult, aGroup, aPath, aOperation); + } + + protected void checkMatchCount(int aCount, AuthorizationRule aRule) { + assertEquals( aCount, ((TestAuthorizationRule)aRule).getMatchCount()); + } + + protected Object createResource(String aPath) { + return new TestResource(aPath); + } + + protected void checkRuleCount(int aCount) { + // Empty + } + + /** + * Several checks to verify the outcome of matching against the first rule. + * + */ + public void testFirstRuleGrants() { + assertTrue( _service.isAllowed(createResource("/oni/xyz.jpg"), new ReadOperation())); + checkMatchCount(1, _rule1); + assertTrue(_service.isAllowed(createResource("/oni/xyz.jpg"), new WriteOperation())); + checkMatchCount(2, _rule1); + assertTrue(_service.isAllowed(createResource("/oni/xyz.jpg"), new DeleteOperation())); + checkMatchCount(3, _rule1); + assertTrue(_service.isAllowed(createResource("/oni/xyz.jpg"), new CreateOperation())); + checkMatchCount(4, _rule1); + checkMatchCount(0, _rule2); + checkMatchCount(0, _rule3); + } + + /** + * Verify that a match with the second rule leads to a denial of authorization. + * + */ + public void testSecondRuleDenies() { + assertFalse(_service.isAllowed(createResource("/abc/xyz.jpg"), new ReadOperation())); + checkMatchCount(0, _rule1); + checkMatchCount(1, _rule2); + checkMatchCount(0, _rule3); + } + + /** + * Verifies that the third rule is used when appropriate and that it grants access. + * + */ + public void testThirdRuleGrants() { + assertTrue(_service.isAllowed(createResource("/abc/xyz.jpg"), new WriteOperation())); + checkMatchCount(0, _rule1); + checkMatchCount(0, _rule2); + checkMatchCount(1, _rule3); + } + + /** + * Removes a rule and checks it is removed. + * + */ + public void testRemoveRule() { + checkRuleCount(3); + assertTrue(_service.isAllowed(createResource("/abc/xyz.jpg"), new WriteOperation())); + _service.removeRule(2); + assertFalse(_service.isAllowed(createResource("/abc/xyz.jpg"), new WriteOperation())); + checkRuleCount(2); + } + + /** + * Inserts a rule and checks it is inserted. + * + */ + public void testInsertRule() { + checkRuleCount(3); + assertFalse(_service.isAllowed(createResource("/janse/xyz.jpg"), new WriteOperation())); + _service.appendRule(createRule(GRANTED, "users", "/janse/", WriteOperation.class)); + assertTrue(_service.isAllowed(createResource("/janse/xyz.jpg"), new WriteOperation())); + checkRuleCount(4); + + } + + /** + * Gets the rules. Verifies that all rules are obtained. + * + */ + public void testGetRules() { + AuthorizationRule[] rules = _service.getRules(); + assertEquals(3, rules.length); + } + + /** + * Verifies that when no rules match, access is denied. + * + */ + public void testNoRulesSupportResource() { + assertFalse(_service.isAllowed(createResource("/xyxyxyxy"), new ReadOperation())); + checkMatchCount(0, _rule1); + checkMatchCount(0, _rule2); + checkMatchCount(0, _rule3); + } +} diff --git a/security/src/test/java/org/wamblee/security/authorization/DefaultOperationRegistryTest.java b/security/src/test/java/org/wamblee/security/authorization/DefaultOperationRegistryTest.java new file mode 100644 index 00000000..07b147b2 --- /dev/null +++ b/security/src/test/java/org/wamblee/security/authorization/DefaultOperationRegistryTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import junit.framework.TestCase; + +/** + * Test of the operation registry. + */ +public class DefaultOperationRegistryTest extends TestCase { + + private OperationRegistry _registry; + + /* (non-Javadoc) + * @see junit.framework.TestCase#setUp() + */ + @Override + protected void setUp() throws Exception { + + _registry = new DefaultOperationRegistry(new Operation[] { + new AllOperation(), + new ReadOperation(), + new WriteOperation(), + new DeleteOperation(), + new CreateOperation() + }); + } + + /** + * Tests encoding and decoding of no operations. + * + */ + public void testEncodeDecodeNooperations() { + assertEquals("", _registry.encode(new Operation[0])); + assertEquals(0, _registry.decode(Object.class, "").length); + } + + /** + * Verifies that encoding of operations into a string works. + * + */ + public void testEncode() { + assertEquals("read,write", _registry.encode(new Operation[] { new ReadOperation(), new WriteOperation() })); + } + + /** + * Verifies that decoding of operation from a string works. + * + */ + public void testDecode() { + Operation[] operations = _registry.decode(Object.class, "read,write"); + assertTrue( operations[0] instanceof ReadOperation); + assertTrue( operations[1] instanceof WriteOperation); + } + + /** + * Verifies that an IllegalArgumentException occurs when attempting to decode + * an operation that is not known. + * + */ + public void testDecodeUnknownOperation() { + try { + _registry.decode(Object.class, "bla"); + fail(); + } catch (IllegalArgumentException e) { + // ok + } + } +} diff --git a/security/src/test/java/org/wamblee/security/authorization/RegexpPathConditionTest.java b/security/src/test/java/org/wamblee/security/authorization/RegexpPathConditionTest.java new file mode 100644 index 00000000..92a2f08f --- /dev/null +++ b/security/src/test/java/org/wamblee/security/authorization/RegexpPathConditionTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import junit.framework.TestCase; + +/** + * Test for regular expression matching. + */ +public class RegexpPathConditionTest extends TestCase { + + /** + * Various tests. + * + */ + public void testMatch() { + PathCondition cond = new RegexpPathCondition("abc"); + assertTrue(cond.matches("abc")); + assertFalse(cond.matches("xabcx")); + cond = new RegexpPathCondition("/[a-z]*/.*"); + assertTrue(cond.matches("/hallo/xyz")); + } +} diff --git a/security/src/test/java/org/wamblee/security/authorization/StartsWithPathConditionTest.java b/security/src/test/java/org/wamblee/security/authorization/StartsWithPathConditionTest.java new file mode 100644 index 00000000..37547737 --- /dev/null +++ b/security/src/test/java/org/wamblee/security/authorization/StartsWithPathConditionTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import junit.framework.TestCase; + +/** + * Tests for StartsWithPathCondition. + */ +public class StartsWithPathConditionTest extends TestCase { + + /** + * Various tests. + */ + public void testMatches() { + PathCondition cond = new StartsWithPathCondition("/hallo"); + assertTrue(cond.matches("/hallo")); + assertTrue(cond.matches("/hallox")); + assertTrue(cond.matches("/hallo/abc")); + } +} diff --git a/security/src/test/java/org/wamblee/security/authorization/TestAuthorizationRule.java b/security/src/test/java/org/wamblee/security/authorization/TestAuthorizationRule.java new file mode 100644 index 00000000..fe02560d --- /dev/null +++ b/security/src/test/java/org/wamblee/security/authorization/TestAuthorizationRule.java @@ -0,0 +1,73 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import static org.wamblee.security.authorization.AuthorizationResult.DENIED; +import static org.wamblee.security.authorization.AuthorizationResult.GRANTED; + +import org.wamblee.usermgt.User; + +/** + * Test authorization rule that also counts the number of times the rule matches. + */ +public class TestAuthorizationRule extends UrlAuthorizationRule { + + /** + * Counts the number of matches. + */ + private int _matches = 0; + + public TestAuthorizationRule( AuthorizationResult aResult, String aGroup, + String aPath, Class aOperation) { + super(aResult, new GroupUserCondition(aGroup), + new StartsWithPathCondition(aPath), TestResource.class, new IsaOperationCondition(aOperation)); + } + + protected TestAuthorizationRule() { + super(); + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.UrlAuthorizationRule#getPath(java.lang.Object) + */ + @Override + protected String getResourcePath(Object aResource) { + return ((TestResource)aResource).getPath(); + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.UrlAuthorizationRule#isAllowed(java.lang.Object, org.wamblee.security.authorization.Operation, org.wamblee.usermgt.UserAccessor) + */ + @Override + public AuthorizationResult isAllowed(Object aResource, Operation anOperation, User aUser) { + + AuthorizationResult result = super.isAllowed(aResource, anOperation, aUser); + if ( result.equals(GRANTED) || result.equals(DENIED)) { + _matches++; + } + return result; + } + + public int getMatchCount() { + return _matches; + } + + public void reset() { + _matches = 0; + } + +} diff --git a/security/src/test/java/org/wamblee/security/authorization/TestResource.java b/security/src/test/java/org/wamblee/security/authorization/TestResource.java new file mode 100644 index 00000000..4708a691 --- /dev/null +++ b/security/src/test/java/org/wamblee/security/authorization/TestResource.java @@ -0,0 +1,33 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +/** + * A test resource for authorization. + */ +public class TestResource { + + private String _path; + + public TestResource(String aPath) { + _path = aPath; + } + + public String getPath() { + return _path; + } +} diff --git a/security/src/test/java/org/wamblee/security/authorization/TestUserAccessor.java b/security/src/test/java/org/wamblee/security/authorization/TestUserAccessor.java new file mode 100644 index 00000000..d6b6f875 --- /dev/null +++ b/security/src/test/java/org/wamblee/security/authorization/TestUserAccessor.java @@ -0,0 +1,64 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import junit.framework.TestCase; + +import org.wamblee.security.encryption.Md5HexMessageDigester; +import org.wamblee.usermgt.Group; +import org.wamblee.usermgt.InMemoryGroupSet; +import org.wamblee.usermgt.InMemoryUserSet; +import org.wamblee.usermgt.RegexpNameValidator; +import org.wamblee.usermgt.User; +import org.wamblee.usermgt.UserAccessor; +import org.wamblee.usermgt.UserAdministration; +import org.wamblee.usermgt.UserAdministrationImpl; +import org.wamblee.usermgt.UserMgtException; +import org.wamblee.usermgt.UserMgtException.Reason; + +/** + * User access that always returns a user that belongs to + * a fixed group. + */ +public class TestUserAccessor implements UserAccessor { + + private static final String USER = "erik"; + private static final String PASSWORD = "abc123"; + private static final String GROUP = "users"; + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAccessor#getCurrentUser() + */ + public User getCurrentUser() { + UserAdministration admin = new UserAdministrationImpl( + new InMemoryUserSet( new RegexpNameValidator(RegexpNameValidator.PASSWORD_PATTERN, Reason.INVALID_PASSWORD, "Password must contain at least 6 characters"), + new Md5HexMessageDigester()), new InMemoryGroupSet(), + new RegexpNameValidator(RegexpNameValidator.ID_PATTERN, Reason.INVALID_USERNAME, "Invalid user"), + new RegexpNameValidator(RegexpNameValidator.ID_PATTERN, Reason.INVALID_GROUPNAME, "Invalid group") + ); + try { + Group group = admin.createGroup(GROUP); + return admin.createUser(USER, PASSWORD, group); + } catch (UserMgtException e) { + TestCase.fail(e.getMessage()); + throw new RuntimeException(e); + } + } + +} diff --git a/security/src/test/java/org/wamblee/security/authorization/UrlAuthorizationRuleTest.java b/security/src/test/java/org/wamblee/security/authorization/UrlAuthorizationRuleTest.java new file mode 100644 index 00000000..9cc92870 --- /dev/null +++ b/security/src/test/java/org/wamblee/security/authorization/UrlAuthorizationRuleTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization; + +import static org.wamblee.security.authorization.AuthorizationResult.GRANTED; +import static org.wamblee.security.authorization.AuthorizationResult.UNDECIDED; +import static org.wamblee.security.authorization.AuthorizationResult.UNSUPPORTED_RESOURCE; +import junit.framework.TestCase; + +import org.wamblee.usermgt.User; + + +/** + * Tests for the {@link org.wamblee.security.authorization.UrlAuthorizationRule}. + */ +public class UrlAuthorizationRuleTest extends TestCase { + + /** + * Constructs the rule with a result of UNDECIDED. Verifies that an IllegalArgumentException + * is thrown. + * + */ + public void testConstructWithUndecidedResult() { + try { + new TestAuthorizationRule(UNDECIDED, "users", "/path", ReadOperation.class); + fail(); + } catch (IllegalArgumentException e) { + // ok + } + } + + /** + * Constructs the rule with a result of UNSUPPORTED_RESOURCE. Verifies that an IllegalArgumentException + * is thrown. + * + */ + public void testConstructWithUnsupportedResult() { + try { + new TestAuthorizationRule(UNSUPPORTED_RESOURCE, "users", "/path", ReadOperation.class); + fail(); + } catch (IllegalArgumentException e) { + // ok + } + } + + /** + * Constructs the authorization rule and applies it to an unsupported object type. + * Verifies that the result is UNSUPPORTED_RESOURCE. + * + */ + public void testUnsupportedObject() { + AuthorizationRule rule = new TestAuthorizationRule(GRANTED, "users", "/path", ReadOperation.class); + assertEquals(UNSUPPORTED_RESOURCE, rule.isAllowed("hello", new ReadOperation(), new TestUserAccessor().getCurrentUser())); + } + + public void testMatchingScenarios() { + AuthorizationRule rule = new TestAuthorizationRule(GRANTED, "users", "/path/", ReadOperation.class); + User user = new TestUserAccessor().getCurrentUser(); + + // everything matches + assertEquals(GRANTED, rule.isAllowed(new TestResource("/path/a"), new ReadOperation(), user)); + assertEquals(GRANTED, rule.isAllowed(new TestResource("/path/"), new ReadOperation(), user)); + + // path does not match. + assertEquals(UNDECIDED, rule.isAllowed(new TestResource("/path"), new ReadOperation(), user)); + + // operation does not match. + assertEquals(UNDECIDED, rule.isAllowed(new TestResource("/path/"), new WriteOperation(), user)); + + // group does not match. + AuthorizationRule rule2 = new TestAuthorizationRule(GRANTED, "users2", "/path/", ReadOperation.class); + assertEquals(UNDECIDED, rule2.isAllowed(new TestResource("/path/a"), new ReadOperation(), user)); + } + +} diff --git a/security/src/test/java/org/wamblee/security/authorization/hibernate/AuthorizationMappingFiles.java b/security/src/test/java/org/wamblee/security/authorization/hibernate/AuthorizationMappingFiles.java new file mode 100644 index 00000000..cecce732 --- /dev/null +++ b/security/src/test/java/org/wamblee/security/authorization/hibernate/AuthorizationMappingFiles.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization.hibernate; + +import org.wamblee.usermgt.UsermgtHibernateMappingFiles; + +/** + * Mapping files for authorization. + */ +public class AuthorizationMappingFiles extends UsermgtHibernateMappingFiles { + + public AuthorizationMappingFiles() { + super(new String[]{ "hbm/AuthorizationRule.hbm.xml", "hbm/UserCondition.hbm.xml", + "hbm/AuthorizationService.hbm.xml", "hbm/OperationCondition.hbm.xml", "hbm/PathCondition.hbm.xml", + "hbm/TestAuthorizationRule.hbm.xml" }); + } +} diff --git a/security/src/test/java/org/wamblee/security/authorization/hibernate/AuthorizationSpringConfigFiles.java b/security/src/test/java/org/wamblee/security/authorization/hibernate/AuthorizationSpringConfigFiles.java new file mode 100644 index 00000000..6ecbff86 --- /dev/null +++ b/security/src/test/java/org/wamblee/security/authorization/hibernate/AuthorizationSpringConfigFiles.java @@ -0,0 +1,30 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization.hibernate; + +import org.wamblee.usermgt.UsermgtSpringConfigFiles; + +/** + * + */ +public class AuthorizationSpringConfigFiles extends UsermgtSpringConfigFiles { + + public AuthorizationSpringConfigFiles() { + super(new String[] { "spring/test.org.wamblee.security.authorization.xml" }); + } + +} diff --git a/security/src/test/java/org/wamblee/security/authorization/hibernate/PersistentAuthorizationServiceTest.java b/security/src/test/java/org/wamblee/security/authorization/hibernate/PersistentAuthorizationServiceTest.java new file mode 100644 index 00000000..93758b12 --- /dev/null +++ b/security/src/test/java/org/wamblee/security/authorization/hibernate/PersistentAuthorizationServiceTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.authorization.hibernate; + +import java.sql.SQLException; + +import org.apache.log4j.Logger; +import org.springframework.orm.hibernate3.HibernateTemplate; +import org.wamblee.general.BeanKernel; +import org.wamblee.security.authorization.AuthorizationService; +import org.wamblee.security.authorization.AuthorizationServiceTest; + +/** + * Unit test for the persistent authorization service. + */ +public class PersistentAuthorizationServiceTest extends AuthorizationServiceTest { + + private static final Logger LOGGER = Logger.getLogger(PersistentAuthorizationServiceTest.class); + + private static final String SERVICE_TABLE = "AUTHORIZATION_SERVICE"; + private static final String RULES_TABLE = "AUTHORIZATION_RULES"; + private static final String SERVICE_RULES_TABLE = "AUTHORIZATION_SERVICE_RULES"; + private static final String OPERATIONCOND_TABLE = "OPERATION_CONDITIONS"; + private static final String PATHCOND_TABLE = "PATH_CONDITIONS"; + private static final String USERCOND_TABLE = "USER_CONDITIONS"; + + + public PersistentAuthorizationServiceTest() { + super(AuthorizationSpringConfigFiles.class, AuthorizationMappingFiles.class); + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.AuthorizationServiceTest#createService() + */ + @Override + protected AuthorizationService createService() { + PersistentAuthorizationService service = new PersistentAuthorizationService("DEFAULT", + BeanKernel.getBeanFactory().find(HibernateTemplate.class), createUserAccessor(), 10000); + return service; + } + + /* (non-Javadoc) + * @see org.wamblee.security.authorization.AuthorizationServiceTest#checkRuleCount(int) + */ + @Override + protected void checkRuleCount(int aCount) { + try { + assertEquals(1, getTableSize(SERVICE_TABLE)); + assertEquals(aCount, getTableSize(RULES_TABLE)); + assertEquals(aCount, getTableSize(SERVICE_RULES_TABLE)); + assertEquals(aCount, getTableSize(USERCOND_TABLE)); + assertEquals(aCount, getTableSize(PATHCOND_TABLE)); + assertEquals(aCount, getTableSize(OPERATIONCOND_TABLE)); + } catch (SQLException e) { + throw new RuntimeException(e); + } + + } + + public void testPerformance() { + + PersistentAuthorizationService service = (PersistentAuthorizationService)getService(); + + int n = 1000; + long time = System.currentTimeMillis(); + for (int i = 0; i < n; i++) { + testFirstRuleGrants(); + resetTestRules(); + testSecondRuleDenies(); + resetTestRules(); + testThirdRuleGrants(); + resetTestRules(); + testNoRulesSupportResource(); + } + LOGGER.info("Executed " + 4*n + " authorization checks in " + (float)(System.currentTimeMillis()-time)/(float)1000 + " seconds."); + } +} diff --git a/security/src/test/java/org/wamblee/security/encryption/MessageDigesterTest.java b/security/src/test/java/org/wamblee/security/encryption/MessageDigesterTest.java new file mode 100644 index 00000000..78abdde8 --- /dev/null +++ b/security/src/test/java/org/wamblee/security/encryption/MessageDigesterTest.java @@ -0,0 +1,29 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.security.encryption; + +import junit.framework.TestCase; + +/** + * Tests for the message digester. + */ +public class MessageDigesterTest extends TestCase { + + public void testMd5HexEncoding() { + assertEquals("e99a18c428cb38d5f260853678922e03", new Md5HexMessageDigester().hash("abc123")); + } +} diff --git a/security/src/test/java/org/wamblee/usermgt/InMemoryGroupSetTest.java b/security/src/test/java/org/wamblee/usermgt/InMemoryGroupSetTest.java new file mode 100644 index 00000000..a751a103 --- /dev/null +++ b/security/src/test/java/org/wamblee/usermgt/InMemoryGroupSetTest.java @@ -0,0 +1,199 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.sql.SQLException; +import java.util.Set; + +import org.wamblee.persistence.hibernate.HibernateMappingFiles; +import org.wamblee.test.SpringConfigFiles; +import org.wamblee.test.SpringTestCase; + +/** + * Tests the inmemory group set. Intended to be subclassed for other + * implementations of group set. + */ +public class InMemoryGroupSetTest extends SpringTestCase { + + protected GroupSet _groups; + + public InMemoryGroupSetTest() { + super(SpringConfigFiles.class, HibernateMappingFiles.class); + } + + protected InMemoryGroupSetTest(Class aSprings, + Class aMappings) { + super(aSprings, aMappings); + } + + /** + * This method must be overriden in subclasses. + * @return New group set object. + */ + protected GroupSet createGroupSet() { + return new InMemoryGroupSet(); + } + + /* (non-Javadoc) + * @see org.wamblee.test.SpringTestCase#setUp() + */ + @Override + protected void setUp() throws Exception { + super.setUp(); + _groups = createGroupSet(); + checkGroupCount(0); + } + + + + /** + * Additional check to be implemented by a subclass. + * @param aGroup Group to check for existence. + */ + protected void checkGroupExists(String aGroup) throws SQLException { + // Empty + } + + /** + * Additional check to be implemented by a subclass. + * @param aGroup Group to check for non-existence. + */ + protected void checkGroupNotExists(String aGroup) throws SQLException { + // Empty + } + + /** + * Additional check to be implemented by a subclass. + * @param aSize Expected number of groups. + */ + protected void checkGroupCount(int aSize) throws SQLException { + assertEquals(aSize, _groups.size()); + } + + /** + * Adds a group and verifies that the group is added using + * find(), list(), and contains(). + * + */ + public void testAdd() throws SQLException { + Group group = new Group("group1"); + assertTrue( _groups.add(group) ); + checkGroupExists(group.getName()); + checkGroupCount(1); + Group group2 = _groups.find("group1"); + assertNotNull(group2); + assertEquals(group.getName(), group2.getName()); + Set set = _groups.list(); + assertEquals(1, set.size()); + assertTrue(set.contains(group)); + } + + /** + * Tries to find a non-existing group. Verifies that null is + * returned. + * + */ + public void testFindUnknownGroup() throws SQLException { + Group group1 = new Group("group1"); + Group group2 = new Group("group2"); + _groups.add(group1); + _groups.add(group2); + checkGroupExists(group1.getName()); + checkGroupExists(group2.getName()); + + assertNull( _groups.find("group3") ); + checkGroupNotExists("group3"); + } + + /** + * Adds duplicate group. Verifies that the existing group is left untouched. + */ + public void testAddDuplicateGroup() throws SQLException { + Group group1 = new Group("group1"); + _groups.add(group1); + + assertEquals(1, _groups.list().size()); + assertTrue(_groups.contains(group1)); + group1 = new Group("group1"); + assertFalse(_groups.add(group1)); + assertEquals(1, _groups.list().size()); + + checkGroupExists(group1.getName()); + checkGroupCount(1); + } + + /** + * Removes a group. Verifies that the group is + * removed and the return value is true. + * + */ + public void testRemoveGroup() throws SQLException { + Group group1 = new Group("group1"); + _groups.add(group1); + assertTrue(_groups.contains(group1)); + checkGroupCount(1); + + assertTrue(_groups.remove(group1)); + assertFalse(_groups.contains(group1)); + assertNull(_groups.find(group1.getName())); + assertEquals(0, _groups.list().size()); + checkGroupCount(0); + } + + /** + * Removes a non-existing group. Verifies that no groups are + * removed an that the return value is true. + * + */ + public void testRemoveNonExistingGroup() throws SQLException { + Group group1 = new Group("group1"); + _groups.add(group1); + checkGroupCount(1); + Group nonExistingGroup = new Group("group2"); + nonExistingGroup.setPrimaryKey(new Long(1000)); + nonExistingGroup.setPersistedVersion(1000); + assertFalse(_groups.remove(nonExistingGroup)); + assertTrue(_groups.contains(group1)); + assertEquals(1, _groups.list().size()); + checkGroupCount(1); + } + + /** + * Adds a number of groups to the set and verifies that list() + * returns them all. + * + */ + public void testList() throws SQLException { + Group group1 = new Group("group1"); + Group group2 = new Group("group2"); + Group group3 = new Group("group3"); + assertTrue(_groups.add(group1)); + assertTrue(_groups.add(group2)); + assertTrue(_groups.add(group3)); + + checkGroupExists(group1.getName()); + checkGroupExists(group2.getName()); + checkGroupExists(group3.getName()); + + Set set = _groups.list(); + assertTrue(set.contains(group1)); + assertTrue(set.contains(group2)); + assertTrue(set.contains(group3)); + + checkGroupCount(3); + } +} diff --git a/security/src/test/java/org/wamblee/usermgt/InMemoryUserSetTest.java b/security/src/test/java/org/wamblee/usermgt/InMemoryUserSetTest.java new file mode 100644 index 00000000..0e3c7592 --- /dev/null +++ b/security/src/test/java/org/wamblee/usermgt/InMemoryUserSetTest.java @@ -0,0 +1,322 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.sql.SQLException; +import java.util.Set; + +import org.wamblee.persistence.hibernate.HibernateMappingFiles; +import org.wamblee.security.encryption.Md5HexMessageDigester; +import org.wamblee.test.SpringConfigFiles; +import org.wamblee.test.SpringTestCase; +import org.wamblee.usermgt.UserMgtException.Reason; + +/** + * Tests the inmemory user set. Intended to be subclassed for other + * implementations of user set. + */ +public class InMemoryUserSetTest extends SpringTestCase { + + protected static final String PASSWORD = "abc123"; + + private UserSet _users; + private GroupSet _groups; + + private Group _group; + + public InMemoryUserSetTest() { + super(SpringConfigFiles.class, HibernateMappingFiles.class); + } + + protected InMemoryUserSetTest(Class aSprings, + Class aMappings) { + super(aSprings, aMappings); + } + + /** + * This method must be overriden in subclasses. + * @return New user set object. + */ + protected UserSet createUserSet() { + return new InMemoryUserSet( new RegexpNameValidator(RegexpNameValidator.PASSWORD_PATTERN, Reason.INVALID_PASSWORD, "Password must contain at least 6 characters"), + new Md5HexMessageDigester()); + } + + /** + * This method must be overriden in subclasses. + * @return New group set object. + */ + protected GroupSet createGroupSet() { + return new InMemoryGroupSet(); + } + + /* (non-Javadoc) + * @see org.wamblee.test.SpringTestCase#setUp() + */ + @Override + protected void setUp() throws Exception { + super.setUp(); + _users = createUserSet(); + _groups = createGroupSet(); + _group = new Group("group0"); + _groups.add(_group); + checkUserCount(0); + + } + + protected UserSet getUsers() { + return _users; + } + + protected GroupSet getGroups() { + return _groups; + } + + protected Group createGroup(String aName) { + return new Group(aName); + } + + protected User createUser(String aName, String aPassword, Group aGroup) throws UserMgtException { + return UsermgtTestUtils.createUser(aName, aPassword, aGroup); + } + + protected void addUserToGroup(User aUser, Group aGroup) throws UserMgtException { + aUser.addGroup(aGroup); + } + + protected void removeUserFromGroup(User aUser, Group aGroup ) throws UserMgtException { + aUser.removeGroup(aGroup); + } + + /** + * Additional check to be implemented by a subclass. + * @param aUser User to check for existence. + */ + protected void checkUserExists(String aUser) throws SQLException { + // Empty + } + + /** + * Additional check to be implemented by a subclass. + * @param aUser User to check for non-existence. + */ + protected void checkUserNotExists(String aUser) throws SQLException { + // Empty + } + + /** + * Additional check to be implemented by a subclass. + * @param aSize Expected number of users. + */ + protected void checkUserCount(int aSize) throws SQLException { + assertEquals(aSize, _users.size()); + } + + /** + * Additional check to be implemented by a subclass. + * @param aUser User to check for existence. + */ + protected void checkGroupExists(String aUser) throws SQLException { + // Empty + } + + /** + * Additional check to be implemented by a subclass. + * @param aUser User to check for non-existence. + */ + protected void checkGroupNotExists(String aUser) throws SQLException { + // Empty + } + + /** + * Additional check to be implemented by a subclass. + * @param aSize Expected number of users. + */ + protected void checkGroupCount(int aSize) throws SQLException { + // Empty + } + + + /** + * Adds a user and verifies that the user is added using + * find(), list(), and contains(). + * + */ + public void testAdd() throws SQLException, UserMgtException { + User user = createUser("user1", PASSWORD, _group); + assertTrue( _users.add(user) ); + checkUserExists(user.getName()); + checkUserCount(1); + User user2 = _users.find("user1"); + assertNotNull(user2); + assertEquals(user.getName(), user2.getName()); + Set set = _users.list(); + assertEquals(1, set.size()); + assertTrue(set.contains(user)); + } + + /** + * Tries to find a non-existing user. Verifies that null is + * returned. + * + */ + public void testFindUnknownUser() throws SQLException, UserMgtException { + User user1 = createUser("user1", PASSWORD, _group); + User user2 = createUser("user2", PASSWORD, _group); + _users.add(user1); + _users.add(user2); + checkUserExists(user1.getName()); + checkUserExists(user2.getName()); + + assertNull( _users.find("user3") ); + checkUserNotExists("user3"); + } + + /** + * Adds duplicate user. Verifies that the existing user is left untouched. + */ + public void testAddDuplicateUser() throws SQLException, UserMgtException { + User user1 = createUser("user1", PASSWORD, _group); + _users.add(user1); + + assertEquals(1, _users.list().size()); + assertTrue(_users.contains(user1)); + user1 = createUser("user1", PASSWORD, _group); + assertFalse(_users.add(user1)); + assertEquals(1, _users.list().size()); + + checkUserExists(user1.getName()); + checkUserCount(1); + } + + /** + * Removes a user. Verifies that the user is + * removed and the return value is true. + * + */ + public void testRemoveUser() throws SQLException, UserMgtException { + User user1 = createUser("user1", PASSWORD, _group); + _users.add(user1); + assertTrue(_users.contains(user1)); + checkUserCount(1); + + assertTrue(_users.remove(user1)); + assertFalse(_users.contains(user1)); + assertNull(_users.find(user1.getName())); + assertEquals(0, _users.list().size()); + checkUserCount(0); + } + + /** + * Removes a non-existing user. Verifies that no users are + * removed an that the return value is true. + * + */ + public void testRemoveNonExistingUser() throws SQLException, UserMgtException { + User user1 = createUser("user1", PASSWORD, _group); + _users.add(user1); + checkUserCount(1); + User nonExistingUser = createUser("user2", PASSWORD, _group); + nonExistingUser.setPrimaryKey(new Long(1000)); + nonExistingUser.setPersistedVersion(10); + assertFalse(_users.remove(nonExistingUser)); + assertTrue(_users.contains(user1)); + assertEquals(1, _users.list().size()); + checkUserCount(1); + } + + /** + * Adds a number of users to the set and verifies that list() + * returns them all. + * + */ + public void testList() throws SQLException, UserMgtException { + User user1 = createUser("user1", PASSWORD, _group); + User user2 = createUser("user2", PASSWORD, _group); + User user3 = createUser("user3", PASSWORD, _group); + assertTrue(_users.add(user1)); + assertTrue(_users.add(user2)); + assertTrue(_users.add(user3)); + + checkUserExists(user1.getName()); + checkUserExists(user2.getName()); + checkUserExists(user3.getName()); + + Set set = _users.list(); + assertTrue(set.contains(user1)); + assertTrue(set.contains(user2)); + assertTrue(set.contains(user3)); + + checkUserCount(3); + } + + /** + * Adds several users to different groups and verifies that + * the correct users are returned when looking for users in + * different groups. + * @throws SQLException + */ + public void testListByGroup() throws SQLException, UserMgtException { + Group group1 = new Group("group1"); + Group group2 = new Group("group2"); + Group group3 = new Group("group3"); + _groups.add(group1); + _groups.add(group2); + _groups.add(group3); + + // user1 user2 user3 + // group1 y + // group2 y y + // group3 y y y + + User user1 = createUser("user1", PASSWORD, group1); + user1.addGroup(group2); + user1.addGroup(group3); + User user2 = createUser("user2", PASSWORD, group2); + user2.addGroup(group3); + User user3 = createUser("user3", PASSWORD, group3); + _users.add(user1); + _users.add(user2); + _users.add(user3); + + checkUserExists(user1.getName()); + checkUserExists(user2.getName()); + checkUserExists(user3.getName()); + + checkGroupExists(group1.getName()); + checkGroupExists(group2.getName()); + checkGroupExists(group3.getName()); + + checkUserCount(3); + checkGroupCount(3+1); // also count the group that was created in the setUp(). + + Set list = _users.list(group1); + assertTrue(list.contains(user1)); + assertEquals(1, list.size()); + + list = _users.list(group2); + assertTrue(list.contains(user1)); + assertTrue(list.contains(user2)); + assertEquals(2, list.size()); + + list = _users.list(group3); + assertTrue(list.contains(user1)); + assertTrue(list.contains(user2)); + assertTrue(list.contains(user3)); + assertEquals(3, list.size()); + } +} diff --git a/security/src/test/java/org/wamblee/usermgt/UserAdministrationImplTest.java b/security/src/test/java/org/wamblee/usermgt/UserAdministrationImplTest.java new file mode 100644 index 00000000..12834c63 --- /dev/null +++ b/security/src/test/java/org/wamblee/usermgt/UserAdministrationImplTest.java @@ -0,0 +1,611 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.util.Set; + +import org.apache.log4j.Logger; +import org.wamblee.persistence.hibernate.HibernateMappingFiles; +import org.wamblee.security.encryption.Md5HexMessageDigester; +import org.wamblee.test.SpringConfigFiles; +import org.wamblee.test.SpringTestCase; +import org.wamblee.usermgt.UserMgtException.Reason; + +/** + * Test of user administration implementation. + */ +public class UserAdministrationImplTest extends SpringTestCase { + + private static final Logger LOGGER = Logger + .getLogger(UserAdministrationImplTest.class); + + private static final String USER1 = "piet"; + + private static final String PASS1 = "passpiet"; + + private static final String USER2 = "kees"; + + private static final String PASS2 = "passkees"; + + private static final String GROUP1 = "cyclists"; + + private static final String GROUP2 = "runners"; + + private UserAdministration _admin; + + public UserAdministrationImplTest() { + super(SpringConfigFiles.class, HibernateMappingFiles.class); + } + + public UserAdministrationImplTest( + Class aSprings, + Class aMappings) { + super(aSprings, aMappings); + } + + /* + * (non-Javadoc) + * + * @see junit.framework.TestCase#setUp() + */ + @Override + protected void setUp() throws Exception { + super.setUp(); + _admin = createAdmin(); + } + + protected UserAdministration createAdmin() { + UserSet users = new InMemoryUserSet( new RegexpNameValidator(RegexpNameValidator.PASSWORD_PATTERN, Reason.INVALID_PASSWORD, "Password must contain at least 6 characters"), + new Md5HexMessageDigester()); + GroupSet groups = new InMemoryGroupSet(); + return new UserAdministrationImpl(users, groups, + new RegexpNameValidator(RegexpNameValidator.ID_PATTERN, + Reason.INVALID_USERNAME, "Invalid user"), + new RegexpNameValidator(RegexpNameValidator.ID_PATTERN, + Reason.INVALID_GROUPNAME, "Invalid group")); + } + + protected User createUser(String aName, String aPassword, Group aGroup) throws UserMgtException { + return UsermgtTestUtils.createUser(aName, aPassword, aGroup); + } + + /** + * Constructs the admin, verify it contains no users and no groups. + */ + public void testConstruct() { + assertEquals(0, _admin.getUsers().size()); + assertEquals(0, _admin.getGroups().size()); + assertEquals(0, _admin.getUserCount()); + assertEquals(0, _admin.getGroupCount()); + } + + /** + * Creates a new group. Verifies the group is created correctly and that the + * user is added. + * + */ + public void testCreateGroup() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + assertNotNull(group); + assertEquals(GROUP1, group.getName()); + + Set groups = _admin.getGroups(); + assertEquals(1, groups.size()); + assertEquals(1, _admin.getGroupCount()); + assertTrue(groups.contains(group)); + } + + private void createInvalidGroup(String aUsername) { + try { + _admin.createGroup(aUsername); + fail(); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.INVALID_GROUPNAME, e + .getReason()); + assertEquals(0, _admin.getGroupCount()); + } + } + + /** + * Creates a new group with an invalid name. Verifies that the appropriate + * exception is thrown. + * + * @throws UserMgtException + */ + public void testCreateInvalidGroupName() throws UserMgtException { + createInvalidGroup(""); + createInvalidGroup("0abc"); // should not start with digits + createInvalidGroup("a b"); // should not contain spaces + createInvalidGroup(" aa"); + createInvalidGroup("aa "); + } + + /** + * Creates a new group which conflicts with an existing one. Verifies that + * the UserMgtException is thrown and that no group is added. + * + */ + public void testCreateDuplicateGroup() throws UserMgtException { + _admin.createGroup(GROUP1); + try { + _admin.createGroup(GROUP1); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.DUPLICATE_GROUP, e.getReason()); + assertEquals(1, _admin.getGroupCount()); + return; + } + fail(); + } + + /** + * Creates a new user. Verifies the user is created correctly and that the + * user is added. + * + */ + public void testCreateUser() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + User user = _admin.createUser(USER1, PASS1, group); + assertNotNull(user); + assertEquals(USER1, user.getName()); + user.checkPassword(PASS1); + + Set users = _admin.getUsers(); + assertEquals(1, users.size()); + assertEquals(1, _admin.getUserCount()); + assertTrue(users.contains(user)); + } + + private void createInvalidUser(String aUsername, Group aGroup) { + try { + _admin.createUser(aUsername, "pass", aGroup); + fail(); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.INVALID_USERNAME, e + .getReason()); + assertEquals(0, _admin.getUserCount()); + } + } + + /** + * Constructs users with invalid names. Verifies that the appropriate + * exception is thrown. + * + */ + public void testCreateInvalidUserName() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + createInvalidUser("", group); + createInvalidUser("0abc", group); // should not start with digits + createInvalidUser("a b", group); // should not contain spaces + createInvalidUser(" aa", group); + createInvalidUser("aa ", group); + } + + /** + * Creates a new user which conflicts with an existing one. Verifies that + * the UserMgtException is thrown and that no user is added. + * + */ + public void testCreateDuplicateUser() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + _admin.createUser(USER1, PASS1, group); + try { + _admin.createUser(USER1, PASS2, group); + fail(); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.DUPLICATE_USER, e.getReason()); + assertEquals(1, _admin.getUserCount()); + } + } + + /** + * Gets a known user by name. Verifies the correct user is obtained. + * Verifies that null is returned when trying to obtain an unknown user. + * + */ + public void testGetUser() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + User user = _admin.createUser(USER1, PASS1, group); + User user2 = _admin.getUser(USER1); + assertTrue(user.equals(user2)); + assertNull(_admin.getUser(USER2)); + } + + /** + * Gets a known group by name. Verifies the correct group is obtained. + * Verifies that null is returned when the group is not known. + * + */ + public void testGetGroup() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + Group group2 = _admin.getGroup(GROUP1); + assertTrue(group.equals(group2)); + assertNull(_admin.getGroup(GROUP2)); + } + + /** + * Adds a user to a group. Verifies that the user is added using several API + * calls. Verifies that an exception occurs if the user is not already part + * of the group. + * + */ + public void testAddUserToGroup() throws UserMgtException { + + Group group = _admin.createGroup(GROUP1); + User user = _admin.createUser(USER1, PASS1, group); + Group group2 = _admin.createGroup(GROUP2); + assertTrue(user.isInGroup(group)); + assertFalse(user.isInGroup(group2)); + _admin.addUserToGroup(user, group2); + assertTrue(user.isInGroup(group)); + assertTrue(user.isInGroup(group2)); + Set users = _admin.getUsers(group2); + assertNotNull(users); + assertEquals(1, users.size()); + assertTrue(users.contains(user)); + + try { + _admin.addUserToGroup(user, group); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.USER_ALREADY_IN_GROUP, e + .getReason()); + return; + } + fail(); + } + + /** + * Adds a user to a group where the user does not exist. Verifies that an + * exception occurs. + * + */ + public void testAddUserToGroupUnknownUser() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + User user = createUser(USER1, PASS1, group); + try { + _admin.addUserToGroup(user, group); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.UNKNOWN_USER, e.getReason()); + return; + } + fail(); + } + + /** + * Adds a user to a group where the user does not exist. Verifies that an + * exception occurs. + * + */ + public void testAddUserToGroupUnknownGroup() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + User user = _admin.createUser(USER1, PASS1, group); + Group group2 = new Group(GROUP2); + try { + _admin.addUserToGroup(user, group2); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.UNKNOWN_GROUP, e.getReason()); + return; + } + fail(); + } + + /** + * Removes a user from a group. Verifies that the user is removed from the + * group using several API calls. Verifies that an exception occurs if the + * user not part of the group or if the user is only part of one group. + */ + public void testRemoveUserFromGroup() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + + User user = _admin.createUser(USER1, PASS1, group); + Group group2 = _admin.createGroup(GROUP2); + _admin.addUserToGroup(user, group2); + Set groups = user.getGroups(); + assertEquals(2, groups.size()); + assertTrue(groups.contains(group)); + assertTrue(groups.contains(group2)); + + _admin.removeUserFromGroup(user, group); + groups = user.getGroups(); + assertEquals(1, groups.size()); + assertTrue(groups.contains(group2)); + assertFalse(groups.contains(group)); + } + + /** + * Removes a user from a group where the user is not known. Verifies that an + * exception is thrown. + * + */ + public void testRemoveUserFromGroupUnknownUser() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + User user = createUser(USER1, GROUP1, group); + try { + _admin.removeUserFromGroup(user, group); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.UNKNOWN_USER, e.getReason()); + } + } + + /** + * Removes a user from a group where the group is not known. Verifies that + * an exception is thrown. + * + */ + public void testRemoveUserFromGroupUnknownGroup() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + User user = _admin.createUser(USER1, PASS1, group); + Group group2 = new Group(GROUP2); + try { + _admin.removeUserFromGroup(user, group2); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.UNKNOWN_GROUP, e.getReason()); + } + } + + /** + * Removes a user from a group where the user is only part of one group. + * Verifies that an exception is thrown. + */ + public void testRemoveUserFromGroupOnlyGroup() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + User user = _admin.createUser(USER1, PASS1, group); + try { + _admin.removeUserFromGroup(user, group); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.USER_MUST_BE_IN_A_GROUP, e + .getReason()); + } + } + + /** + * Gets the list of users and groups. Verifies that the correct suers and + * groups are returned. Verifies also that the relations from user to group + * are correct. + * + */ + public void testGetUsersAndGroups() throws UserMgtException { + Group group1 = _admin.createGroup(GROUP1); + Group group2 = _admin.createGroup(GROUP2); + + User user1 = _admin.createUser(USER1, PASS1, group1); + _admin.addUserToGroup(user1, group2); + User user2 = _admin.createUser(USER2, PASS2, group2); + + Set users = _admin.getUsers(); + assertEquals(2, users.size()); + assertTrue(users.contains(user1)); + assertTrue(users.contains(user2)); + + Set groups = _admin.getGroups(); + assertEquals(2, groups.size()); + assertTrue(groups.contains(group1)); + assertTrue(groups.contains(group2)); + + assertTrue(user1.isInGroup(group1)); + assertTrue(user1.isInGroup(group2)); + assertFalse(user2.isInGroup(group1)); + assertTrue(user2.isInGroup(group2)); + + Set groups1 = user1.getGroups(); + assertEquals(2, groups1.size()); + + Set groups2 = user2.getGroups(); + assertEquals(1, groups2.size()); + } + + /** + * Renames a user. Verifies that the user is renamed. Verifies that + * exceptions are thrown when an attempt is made to rename the user to + * itself or to another existing user, or when the group does not exist. + * + */ + public void testRenameUser() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + User user1 = _admin.createUser(USER1, PASS1, group); + _admin.renameUser(user1, USER2); + assertEquals(USER2, user1.getName()); + assertEquals(user1, _admin.getUser(USER2)); + + _admin.createUser(USER1, PASS1, group); + + try { + _admin.renameUser(user1, USER1); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.DUPLICATE_USER, e.getReason()); + + // do a trivial reanem + try { + _admin.renameUser(user1, user1.getName()); + } catch (UserMgtException e2) { + assertEquals(UserMgtException.Reason.TRIVIAL_RENAME, e2 + .getReason()); + return; + } + fail(); + } + fail(); + } + + /** + * Renames a user to a user with an invalid username. Verifies that the + * appropriate exception is thrown. + * + */ + public void testRenameUserInvalidUsername() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + User user1 = _admin.createUser(USER1, PASS1, group); + try { + _admin.renameUser(user1, USER2); + } catch (UserMgtException e) { + assertEquals(e.getReason(), Reason.INVALID_USERNAME); + } + } + + /** + * Renames a group. Verifies that the group is renamed. Verifies that + * exceptions are thrown when an attempt is made to rename the group to + * itself or to another existing group or when the group does not exist. + * + */ + public void testRenameGroup() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + _admin.renameGroup(group, GROUP2); + assertEquals(GROUP2, group.getName()); + assertEquals(group, _admin.getGroup(GROUP2)); + + _admin.createGroup(GROUP1); + try { + _admin.renameGroup(group, GROUP1); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.DUPLICATE_GROUP, e.getReason()); + + // do a trivial reanem + try { + _admin.renameGroup(group, group.getName()); + } catch (UserMgtException e2) { + assertEquals(UserMgtException.Reason.TRIVIAL_RENAME, e2 + .getReason()); + return; + } + fail(); + return; + } + fail(); + } + + /** + * Renames a group to a group with an invalid name. Verifies that the + * appropriate exception is thrown. + * + */ + public void testRenameGroupInvalidGroupname() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + try { + _admin.renameGroup(group, "a b"); + } catch (UserMgtException e) { + assertEquals(e.getReason(), Reason.INVALID_GROUPNAME); + } + } + + /** + * Removes a user. Verifies that the user is removed. Verifies that the an + * exception is thrown when the user does not exist. + * + */ + public void testRemoveUser() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + User user = _admin.createUser(USER1, PASS1, group); + + assertEquals(1, _admin.getUserCount()); + _admin.removeUser(user); + assertEquals(0, _admin.getUserCount()); + + _admin.createUser(USER1, PASS1, group); + assertEquals(1, _admin.getUserCount()); + + User user2 = createUser(USER2, PASS2, group); + + try { + _admin.removeUser(user2); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.UNKNOWN_USER, e.getReason()); + } + } + + /** + * Removes a group. Verifies that the group is removed. Verifies that the an + * exception is thrown when the group does not exist or if there are still + * users in the group. + * + */ + public void testRemoveGroup() throws UserMgtException { + Group group1 = _admin.createGroup(GROUP1); + assertEquals(1, _admin.getGroupCount()); + _admin.removeGroup(group1); + assertEquals(0, _admin.getGroupCount()); + group1 = _admin.createGroup(GROUP1); + + _admin.createUser(USER1, PASS1, group1); + try { + _admin.removeGroup(group1); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.GROUP_STILL_OCCUPIED, e + .getReason()); + return; + } + fail(); + } + + /** + * Tries to remove an unknown group. Verifies that an exception is thrown. + * + */ + public void testRemoveGroupUnknownGroup() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + Group group2 = new Group(GROUP2); + try { + _admin.removeGroup(group2); + } catch (UserMgtException e) { + assertEquals(UserMgtException.Reason.UNKNOWN_GROUP, e.getReason()); + } + } + + /** + * Changes the password, verifies that this succeeds. + * + * @throws UserMgtException + */ + public void testChangePassword() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + User user = _admin.createUser(USER1, PASS1, group); + user.changePassword(PASS1, PASS2); + + // retrieve the user and verifies the password hasn't changed. + User user2 = _admin.getUser(USER1); + try { + user2.checkPassword(PASS2); + fail(); // password should not have changed already. + } catch (UserMgtException e) { + // ok. + } + + // now notify the admin of the change in the user + _admin.userModified(user); + + user2 = _admin.getUser(USER1); + user2.checkPassword(PASS2); // this time it should succeed. + + } + + /** + * Performance test. Finds a user by name. + * + */ + public void testPerformanceFindUserByName() throws UserMgtException { + Group group = _admin.createGroup(GROUP1); + _admin.createUser(USER1, PASS1, group); + + int n = 1000; + long time = System.currentTimeMillis(); + for (int i = 0; i < n; i++) { + _admin.getUser(USER1); + } + LOGGER.info("Looked up a user " + n + " times in " + + (float) (System.currentTimeMillis() - time) / 1000.0); + } + +} diff --git a/security/src/test/java/org/wamblee/usermgt/UsermgtHibernateMappingFiles.java b/security/src/test/java/org/wamblee/usermgt/UsermgtHibernateMappingFiles.java new file mode 100644 index 00000000..611afd3f --- /dev/null +++ b/security/src/test/java/org/wamblee/usermgt/UsermgtHibernateMappingFiles.java @@ -0,0 +1,38 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.util.Collections; + +import org.wamblee.persistence.hibernate.HibernateMappingFiles; + +/** + * Hibernate mapping files for user management. + */ +public class UsermgtHibernateMappingFiles extends HibernateMappingFiles { + + public UsermgtHibernateMappingFiles() { + super(new String[] { + "hbm/Group.hbm.xml", "hbm/User.hbm.xml" + }); + } + + public UsermgtHibernateMappingFiles(String[] aFiles) { + this(); + Collections.addAll(this, aFiles); + } +} diff --git a/security/src/test/java/org/wamblee/usermgt/UsermgtSpringConfigFiles.java b/security/src/test/java/org/wamblee/usermgt/UsermgtSpringConfigFiles.java new file mode 100644 index 00000000..8ef20cee --- /dev/null +++ b/security/src/test/java/org/wamblee/usermgt/UsermgtSpringConfigFiles.java @@ -0,0 +1,39 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import java.util.Collections; + +import org.wamblee.test.SpringConfigFiles; + +/** + * Spring config files for user management. + */ +public class UsermgtSpringConfigFiles extends SpringConfigFiles { + + public UsermgtSpringConfigFiles() { + super(new String[] { "spring/test.org.wamblee.security.properties.xml", + "spring/test.org.wamblee.security.datasource.xml", + "spring/test.org.wamblee.security.database.xml", + "spring/test.org.wamblee.security.usermgt.xml" }); + } + + public UsermgtSpringConfigFiles(String[] aFiles) { + this(); + Collections.addAll(this, aFiles); + } +} diff --git a/security/src/test/java/org/wamblee/usermgt/UsermgtTestUtils.java b/security/src/test/java/org/wamblee/usermgt/UsermgtTestUtils.java new file mode 100644 index 00000000..3075034f --- /dev/null +++ b/security/src/test/java/org/wamblee/usermgt/UsermgtTestUtils.java @@ -0,0 +1,60 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt; + +import org.wamblee.security.encryption.Md5HexMessageDigester; +import org.wamblee.usermgt.UserMgtException.Reason; + +/** + * User management test utilities. + */ +public class UsermgtTestUtils { + + private static final String DUMMY_GROUP = "dummygroup"; + private static final String DUMMY_PASSWD = "dummypasswd"; + + public static Group createGroup(String aName) { + return new Group(aName); + } + + public static User createUser(String aUsername) throws UserMgtException { + return createUser(aUsername, DUMMY_GROUP); + } + + public static User createUser(String aUsername, String aGroup) throws UserMgtException { + return createUser(aUsername, createGroup(aGroup)); + } + + public static User createUser(String aUsername, Group aGroup) throws UserMgtException { + return createUser(aUsername, DUMMY_PASSWD, aGroup); + } + + public static User createUser(String aName, String aPassword, Group aGroup) throws UserMgtException { + return new User(aName, aPassword, aGroup, + new RegexpNameValidator(RegexpNameValidator.PASSWORD_PATTERN, + Reason.INVALID_PASSWORD, "Password must be at least 6 chars"), + new Md5HexMessageDigester()); + } + + public static void addUserToGroup(User aUser, Group aGroup) throws UserMgtException { + aUser.addGroup(aGroup); + } + + public static void removeUserFromGroup(User aUser, Group aGroup) throws UserMgtException { + aUser.removeGroup(aGroup); + } +} diff --git a/security/src/test/java/org/wamblee/usermgt/hibernate/HibernateGroupSetTest.java b/security/src/test/java/org/wamblee/usermgt/hibernate/HibernateGroupSetTest.java new file mode 100644 index 00000000..bc1e67d1 --- /dev/null +++ b/security/src/test/java/org/wamblee/usermgt/hibernate/HibernateGroupSetTest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt.hibernate; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import org.wamblee.general.BeanKernel; +import org.wamblee.test.TestTransactionCallback; +import org.wamblee.usermgt.GroupSet; +import org.wamblee.usermgt.InMemoryGroupSetTest; +import org.wamblee.usermgt.UsermgtHibernateMappingFiles; +import org.wamblee.usermgt.UsermgtSpringConfigFiles; + +/** + * Tests for {@link org.wamblee.usermgt.hibernate.HibernateGroupSet} + */ +public class HibernateGroupSetTest extends InMemoryGroupSetTest { + + private static final String GROUP_TABLE = "GROUPS"; + + private static final String GROUP_QUERY = "select * from " + GROUP_TABLE + " where name = ?"; + + public HibernateGroupSetTest() { + super(UsermgtSpringConfigFiles.class, UsermgtHibernateMappingFiles.class); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryGroupSetTest#checkGroupCount(int) + */ + @Override + protected void checkGroupCount(int aSize) throws SQLException { + super.flush(); + super.checkGroupCount(aSize); + assertEquals(aSize, getTableSize(GROUP_TABLE)); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryGroupSetTest#checkGroupExists(java.lang.String) + */ + @Override + protected void checkGroupExists(final String aGroup) throws SQLException { + flush(); + Map result = + executeTransaction(new TestTransactionCallback() { + /* (non-Javadoc) + * @see org.wamblee.test.TestTransactionCallback#execute() + */ + @Override + public Map execute() throws Exception { + ResultSet result = executeQuery(GROUP_QUERY, aGroup); + Map res = new HashMap(); + res.put("result", countResultSet(result)); + return res; + } + }); + + int count = result.get("result"); + assertEquals(1, count); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryGroupSetTest#checkGroupNotExists(java.lang.String) + */ + @Override + protected void checkGroupNotExists(String aGroup) throws SQLException { + flush(); + ResultSet result = executeQuery(GROUP_QUERY, aGroup); + assertEquals(0, countResultSet(result)); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryGroupSetTest#createGroupSet() + */ + @Override + protected GroupSet createGroupSet() { + return BeanKernel.getBeanFactory().find(GroupSet.class); + } + +} diff --git a/security/src/test/java/org/wamblee/usermgt/hibernate/HibernateUserAdministrationTest.java b/security/src/test/java/org/wamblee/usermgt/hibernate/HibernateUserAdministrationTest.java new file mode 100644 index 00000000..07a770c0 --- /dev/null +++ b/security/src/test/java/org/wamblee/usermgt/hibernate/HibernateUserAdministrationTest.java @@ -0,0 +1,98 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt.hibernate; + +import java.lang.reflect.Method; +import java.sql.SQLException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wamblee.cache.Cache; +import org.wamblee.general.BeanKernel; +import org.wamblee.test.TestTransactionCallbackWithoutResult; +import org.wamblee.usermgt.UserAdministration; +import org.wamblee.usermgt.UserAdministrationImplTest; +import org.wamblee.usermgt.UsermgtHibernateMappingFiles; +import org.wamblee.usermgt.UsermgtSpringConfigFiles; + +/** + * User administration tests with persistence based on Hibernate. This executes + * the same test cases as {@link org.wamblee.usermgt.UserAdministrationImplTest} + * with in addition, one test case that executes all Hibernate test cases + * separately with each test case in its own transaction. + */ +public class HibernateUserAdministrationTest extends UserAdministrationImplTest { + + private static final Log LOG = LogFactory.getLog(HibernateUserAdministrationTest.class); + + public HibernateUserAdministrationTest() { + super(UsermgtSpringConfigFiles.class, + UsermgtHibernateMappingFiles.class); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.UserAdministrationImplTest#setUp() + */ + @Override + protected void setUp() throws Exception { + super.setUp(); + clearUserCache(); + } + + /* + * (non-Javadoc) + * + * @see org.wamblee.usermgt.UserAdministrationImplTest#createAdmin() + */ + @Override + protected UserAdministration createAdmin() { + return BeanKernel.getBeanFactory().find(UserAdministration.class); + } + + public void testAllTestsInASeparateTransaction() throws SQLException { + + Method[] methods = UserAdministrationImplTest.class.getMethods(); + for (final Method method : methods) { + if (method.getName().startsWith("test")) { + cleanDatabase(); + clearUserCache(); + executeTransaction(new TestTransactionCallbackWithoutResult() { + public void execute() throws Exception { + LOG.info("Running test " + method.getName()); + try { + method.invoke(HibernateUserAdministrationTest.this); + } catch (Throwable t) { + LOG.error("Test " + method.getName() + " failed"); + throw new RuntimeException(t.getMessage(), t); + } + finally { + LOG.info("Test " + method.getName() + " finished"); + } + + } + }); + } + } + } + + /** + * + */ + private void clearUserCache() { + BeanKernel.getBeanFactory().find("userCache", Cache.class).clear(); + } +} diff --git a/security/src/test/java/org/wamblee/usermgt/hibernate/HibernateUserSetTest.java b/security/src/test/java/org/wamblee/usermgt/hibernate/HibernateUserSetTest.java new file mode 100644 index 00000000..0061a436 --- /dev/null +++ b/security/src/test/java/org/wamblee/usermgt/hibernate/HibernateUserSetTest.java @@ -0,0 +1,182 @@ +/* + * Copyright 2005 the original author or authors. + * + * 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. + */ + +package org.wamblee.usermgt.hibernate; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Set; + +import org.wamblee.cache.Cache; +import org.wamblee.general.BeanKernel; +import org.wamblee.usermgt.Group; +import org.wamblee.usermgt.GroupSet; +import org.wamblee.usermgt.InMemoryUserSetTest; +import org.wamblee.usermgt.User; +import org.wamblee.usermgt.UserMgtException; +import org.wamblee.usermgt.UserSet; +import org.wamblee.usermgt.UsermgtHibernateMappingFiles; +import org.wamblee.usermgt.UsermgtSpringConfigFiles; + +/** + * Tests for {@link org.wamblee.usermgt.hibernate.HibernateGroupSet} + */ +public class HibernateUserSetTest extends InMemoryUserSetTest { + + private static final String USER_TABLE = "USERS"; + private static final String GROUP_TABLE = "GROUPS"; + + private static final String USER_QUERY = "select * from " + USER_TABLE + " where name = ?"; + private static final String GROUP_QUERY = "select * from " + GROUP_TABLE + " where name = ?"; + + public HibernateUserSetTest() { + super(UsermgtSpringConfigFiles.class, UsermgtHibernateMappingFiles.class); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryUserSetTest#setUp() + */ + @Override + protected void setUp() throws Exception { + super.setUp(); + clearUserCache(); + } + + /** + * Clears the user cache. + */ + private void clearUserCache() { + BeanKernel.getBeanFactory().find("userCache", Cache.class).clear(); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryGroupSetTest#checkGroupCount(int) + */ + @Override + protected void checkUserCount(int aSize) throws SQLException { + super.flush(); + super.checkUserCount(aSize); + assertEquals(aSize, getTableSize(USER_TABLE)); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryGroupSetTest#checkGroupExists(java.lang.String) + */ + @Override + protected void checkUserExists(String aUser) throws SQLException { + flush(); + ResultSet result = executeQuery(USER_QUERY, aUser); + assertEquals(1, countResultSet(result)); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryGroupSetTest#checkGroupNotExists(java.lang.String) + */ + @Override + protected void checkUserNotExists(String aUser) throws SQLException { + flush(); + ResultSet result = executeQuery(USER_QUERY, aUser); + assertEquals(0, countResultSet(result)); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryGroupSetTest#checkGroupCount(int) + */ + @Override + protected void checkGroupCount(int aSize) throws SQLException { + super.flush(); + assertEquals(aSize, getTableSize(GROUP_TABLE)); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryGroupSetTest#checkGroupExists(java.lang.String) + */ + @Override + protected void checkGroupExists(String aGroup) throws SQLException { + flush(); + + ResultSet result = executeQuery(GROUP_QUERY, aGroup); + assertEquals(1, countResultSet(result)); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryGroupSetTest#checkGroupNotExists(java.lang.String) + */ + @Override + protected void checkGroupNotExists(String aGroup) throws SQLException { + flush(); + ResultSet result = executeQuery(GROUP_QUERY, aGroup); + assertEquals(0, countResultSet(result)); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryGroupSetTest#createGroupSet() + */ + @Override + protected UserSet createUserSet() { + return BeanKernel.getBeanFactory().find(UserSet.class); + } + + /* (non-Javadoc) + * @see org.wamblee.usermgt.InMemoryUserSetTest#createGroupSet() + */ + @Override + protected GroupSet createGroupSet() { + return BeanKernel.getBeanFactory().find(GroupSet.class); + } + + /** + * Reproduction of a bug. + * Create a user which is in group1 + * Add it to a second group group2. + * Remove the user from group1. + * Verify the user is in group2. + */ + public void testVerifyAddRemove() throws SQLException, UserMgtException { + cleanDatabase(); // just to be sure. + GroupSet groups = getGroups(); + assertEquals(0, groups.size()); + Group group1 = createGroup("group1"); + Group group2 = createGroup("group2"); + groups.add(group1); + groups.add(group2); + checkGroupExists("group1"); + checkGroupExists("group2"); + + User user = createUser("user", PASSWORD, group1); + getUsers().add(user); + checkUserExists("user"); + + addUserToGroup(user, group2); + getUsers().userModified(user); + clearUserCache(); + User user2 = getUsers().find("user"); + Set userGroups = user2.getGroups(); + assertTrue(user2.isInGroup("group1")); + assertTrue(user2.isInGroup("group2")); + assertEquals(2, userGroups.size()); + + removeUserFromGroup(user, group1); + getUsers().userModified(user); + clearUserCache(); + user2 = getUsers().find("user"); + userGroups = user2.getGroups(); + assertFalse(user2.isInGroup("group1")); + assertTrue(user2.isInGroup("group2")); + assertEquals(1, userGroups.size()); + } + +} diff --git a/security/src/test/resources/hbm/AuthorizationRule.hbm.xml b/security/src/test/resources/hbm/AuthorizationRule.hbm.xml new file mode 100644 index 00000000..33f5d942 --- /dev/null +++ b/security/src/test/resources/hbm/AuthorizationRule.hbm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/security/src/test/resources/hbm/AuthorizationService.hbm.xml b/security/src/test/resources/hbm/AuthorizationService.hbm.xml new file mode 100644 index 00000000..f8e036ca --- /dev/null +++ b/security/src/test/resources/hbm/AuthorizationService.hbm.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + select service + from org.wamblee.security.authorization.AuthorizationService service + where service.name = :name + + + \ No newline at end of file diff --git a/security/src/test/resources/hbm/Group.hbm.xml b/security/src/test/resources/hbm/Group.hbm.xml new file mode 100644 index 00000000..41e5e6be --- /dev/null +++ b/security/src/test/resources/hbm/Group.hbm.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + from org.wamblee.usermgt.Group grp where grp.name = :name + + + + select count(*) + from org.wamblee.usermgt.Group group + + + \ No newline at end of file diff --git a/security/src/test/resources/hbm/OperationCondition.hbm.xml b/security/src/test/resources/hbm/OperationCondition.hbm.xml new file mode 100644 index 00000000..5ab4b0d8 --- /dev/null +++ b/security/src/test/resources/hbm/OperationCondition.hbm.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/security/src/test/resources/hbm/OperationCondition.hbm.xmlxx b/security/src/test/resources/hbm/OperationCondition.hbm.xmlxx new file mode 100644 index 00000000..250030e0 --- /dev/null +++ b/security/src/test/resources/hbm/OperationCondition.hbm.xmlxx @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/security/src/test/resources/hbm/PageAuthorizationRule.hbm.xml b/security/src/test/resources/hbm/PageAuthorizationRule.hbm.xml new file mode 100644 index 00000000..332e5d83 --- /dev/null +++ b/security/src/test/resources/hbm/PageAuthorizationRule.hbm.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/security/src/test/resources/hbm/PathCondition.hbm.xml b/security/src/test/resources/hbm/PathCondition.hbm.xml new file mode 100644 index 00000000..c43c0a83 --- /dev/null +++ b/security/src/test/resources/hbm/PathCondition.hbm.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/security/src/test/resources/hbm/PhotoAuthorizationRule.hbm.xml b/security/src/test/resources/hbm/PhotoAuthorizationRule.hbm.xml new file mode 100644 index 00000000..fc00dda7 --- /dev/null +++ b/security/src/test/resources/hbm/PhotoAuthorizationRule.hbm.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/security/src/test/resources/hbm/TestAuthorizationRule.hbm.xml b/security/src/test/resources/hbm/TestAuthorizationRule.hbm.xml new file mode 100644 index 00000000..7f796f58 --- /dev/null +++ b/security/src/test/resources/hbm/TestAuthorizationRule.hbm.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/security/src/test/resources/hbm/User.hbm.xml b/security/src/test/resources/hbm/User.hbm.xml new file mode 100644 index 00000000..eefbfa22 --- /dev/null +++ b/security/src/test/resources/hbm/User.hbm.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + from org.wamblee.usermgt.User user where user.name = :name + + + + select user + from org.wamblee.usermgt.User user + join user.groupSet grp + where grp.name = :name + + + + select count(*) + from org.wamblee.usermgt.User user + + + + \ No newline at end of file diff --git a/security/src/test/resources/hbm/UserCondition.hbm.xml b/security/src/test/resources/hbm/UserCondition.hbm.xml new file mode 100644 index 00000000..fc83423d --- /dev/null +++ b/security/src/test/resources/hbm/UserCondition.hbm.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/security/src/test/resources/hbm/ehcache.xml b/security/src/test/resources/hbm/ehcache.xml new file mode 100644 index 00000000..ee79950d --- /dev/null +++ b/security/src/test/resources/hbm/ehcache.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + diff --git a/security/src/test/resources/hbm/hibernate.properties b/security/src/test/resources/hbm/hibernate.properties new file mode 100644 index 00000000..42491b2c --- /dev/null +++ b/security/src/test/resources/hbm/hibernate.properties @@ -0,0 +1,20 @@ + +################################################################################### +# dialect +################################################################################### +hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect + +################################################################################### +# debugging settings: Log4j configuration can provide more detail. +################################################################################### +hibernate.show_sql=false + +################################################################################### +# hibernate cache provider +################################################################################### +hibernate.cache.provider=org.hibernate.cache.EhCacheProvider + +################################################################################### +# query cache +################################################################################### +hibernate.cache.use_query_cache=true \ No newline at end of file diff --git a/security/src/test/resources/properties/test.org.wamblee.security.database.properties b/security/src/test/resources/properties/test.org.wamblee.security.database.properties new file mode 100644 index 00000000..4c2ddd98 --- /dev/null +++ b/security/src/test/resources/properties/test.org.wamblee.security.database.properties @@ -0,0 +1,8 @@ + + +# Database properties for test runs in a J2SE environment. + +database.driver=com.mysql.jdbc.Driver +database.url=jdbc:mysql://localhost:3306/test +database.username=erik +database.password=abc123 \ No newline at end of file diff --git a/security/src/test/resources/properties/test.org.wamblee.security.ehcache.xml b/security/src/test/resources/properties/test.org.wamblee.security.ehcache.xml new file mode 100644 index 00000000..ee79950d --- /dev/null +++ b/security/src/test/resources/properties/test.org.wamblee.security.ehcache.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + diff --git a/security/src/test/resources/properties/test.org.wamblee.security.hibernate.properties b/security/src/test/resources/properties/test.org.wamblee.security.hibernate.properties new file mode 100644 index 00000000..42491b2c --- /dev/null +++ b/security/src/test/resources/properties/test.org.wamblee.security.hibernate.properties @@ -0,0 +1,20 @@ + +################################################################################### +# dialect +################################################################################### +hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect + +################################################################################### +# debugging settings: Log4j configuration can provide more detail. +################################################################################### +hibernate.show_sql=false + +################################################################################### +# hibernate cache provider +################################################################################### +hibernate.cache.provider=org.hibernate.cache.EhCacheProvider + +################################################################################### +# query cache +################################################################################### +hibernate.cache.use_query_cache=true \ No newline at end of file diff --git a/security/src/test/resources/properties/test.org.wamblee.security.usermgt.properties b/security/src/test/resources/properties/test.org.wamblee.security.usermgt.properties new file mode 100644 index 00000000..aa84735d --- /dev/null +++ b/security/src/test/resources/properties/test.org.wamblee.security.usermgt.properties @@ -0,0 +1,5 @@ + +############################################################################## +# Name of the administrators group +############################################################################## +org.wamblee.security.admingroup=administrators diff --git a/security/src/test/resources/spring/test.org.wamblee.security.authorization.xml b/security/src/test/resources/spring/test.org.wamblee.security.authorization.xml new file mode 100644 index 00000000..fe1cb4a0 --- /dev/null +++ b/security/src/test/resources/spring/test.org.wamblee.security.authorization.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + DEFAULT + + + 10000 + + + + + + + + + administrators + + + \ No newline at end of file diff --git a/security/src/test/resources/spring/test.org.wamblee.security.database.xml b/security/src/test/resources/spring/test.org.wamblee.security.database.xml new file mode 100644 index 00000000..af51a1e3 --- /dev/null +++ b/security/src/test/resources/spring/test.org.wamblee.security.database.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + ${hibernate.dialect} + ${hibernate.cache.provider} + ${hibernate.show_sql} + ${hibernate.cache.use_query_cache} + + + + + + + + + + + + + + + + + + diff --git a/security/src/test/resources/spring/test.org.wamblee.security.datasource.xml b/security/src/test/resources/spring/test.org.wamblee.security.datasource.xml new file mode 100644 index 00000000..ec3d26aa --- /dev/null +++ b/security/src/test/resources/spring/test.org.wamblee.security.datasource.xml @@ -0,0 +1,15 @@ + + + + + + + ${database.driver} + ${database.url} + ${database.username} + ${database.password} + + + \ No newline at end of file diff --git a/security/src/test/resources/spring/test.org.wamblee.security.properties.xml b/security/src/test/resources/spring/test.org.wamblee.security.properties.xml new file mode 100644 index 00000000..5fa2f72b --- /dev/null +++ b/security/src/test/resources/spring/test.org.wamblee.security.properties.xml @@ -0,0 +1,16 @@ + + + + + + + + + properties/test.org.wamblee.security.hibernate.properties + properties/test.org.wamblee.security.usermgt.properties + properties/test.org.wamblee.security.database.properties + + + + \ No newline at end of file diff --git a/security/src/test/resources/spring/test.org.wamblee.security.usermgt.xml b/security/src/test/resources/spring/test.org.wamblee.security.usermgt.xml new file mode 100644 index 00000000..a7abe50d --- /dev/null +++ b/security/src/test/resources/spring/test.org.wamblee.security.usermgt.xml @@ -0,0 +1,111 @@ + + + + + + + properties/test.org.wamblee.security.ehcache.xml + + + + + users + + + + + .{5,} + INVALID_PASSWORD + Password must have at least 5 characters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [a-zA-Z]+[a-zA-Z0-9]* + INVALID_USERNAME + + + + + + + [a-zA-Z]+[a-zA-Z0-9]* + INVALID_GROUPNAME + + + + + + + + + + + + + + org.wamblee.usermgt.UserAdministration + usermanagement-lock-advice + + + + + + + + + erik + admin + + + + + + users + ${org.wamblee.security.admingroup} + + + + + + abc123 + abc123 + + + + + \ No newline at end of file diff --git a/support/src/test/java/org/wamblee/test/SpringTestCase.java b/support/src/test/java/org/wamblee/test/SpringTestCase.java index 8b66e0e2..29334e00 100644 --- a/support/src/test/java/org/wamblee/test/SpringTestCase.java +++ b/support/src/test/java/org/wamblee/test/SpringTestCase.java @@ -21,6 +21,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -453,7 +454,7 @@ public class SpringTestCase extends MockObjectTestCase { } /** - * Executes an SQL query within a transaction. + * Executes an SQL query. * * @param aSql * Query to execute. @@ -479,7 +480,7 @@ public class SpringTestCase extends MockObjectTestCase { } /** - * Executes a query within a transaction. See + * Executes a query. See * {@link #setPreparedParam(int, PreparedStatement, Object)}for details on * supported argument types. * @@ -490,22 +491,16 @@ public class SpringTestCase extends MockObjectTestCase { * @return Result set. */ public ResultSet executeQuery(final String aSql, final Object[] aArgs) { - Map results = executeTransaction(new TestTransactionCallback() { - public Map execute() throws Exception { - Connection connection = getConnection(); - - PreparedStatement statement = connection.prepareStatement(aSql); - setPreparedParams(aArgs, statement); + try { + Connection connection = getConnection(); - ResultSet resultSet = statement.executeQuery(); - TreeMap results = new TreeMap(); - results.put("resultSet", resultSet); + PreparedStatement statement = connection.prepareStatement(aSql); + setPreparedParams(aArgs, statement); - return results; - } - }); - - return (ResultSet) results.get("resultSet"); + return statement.executeQuery(); + } catch (SQLException e) { + throw new RuntimeException(e); + } } /** @@ -576,14 +571,14 @@ public class SpringTestCase extends MockObjectTestCase { * @return * @throws SQLException */ - protected int getTableSize(String aTable) throws SQLException { + protected int getTableSize(final String aTable) throws SQLException { + ResultSet resultSet = executeQuery("select * from " + aTable); int count = 0; while (resultSet.next()) { count++; } - return count; }