Initial commit of User Admin contribution, slightly modified POM file and
reformatted source code to match common Felix style. (FELIX-1853)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@888101 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 5290932..e00eb85 100644
--- a/pom.xml
+++ b/pom.xml
@@ -122,6 +122,7 @@
<module>webconsole</module>
<module>webconsole-plugins/event</module>
<module>fileinstall</module>
+ <module>useradmin</module>
<module>ipojo</module>
diff --git a/useradmin/pom.xml b/useradmin/pom.xml
new file mode 100644
index 0000000..5416f1d
--- /dev/null
+++ b/useradmin/pom.xml
@@ -0,0 +1,94 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" 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">
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix-parent</artifactId>
+ <version>1.2.0</version>
+ <relativePath>../../pom/pom.xml</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>bundle</packaging>
+ <name>Apache Felix User Admin Service</name>
+ <version>0.9.0-SNAPSHOT</version>
+ <artifactId>org.apache.felix.useradmin</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>4.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>4.0.0</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>Apache Felix User Admin Service</Bundle-Name>
+ <Bundle-Description>
+ An implementation of the OSGi User Admin Service
+ </Bundle-Description>
+ <Bundle-Activator>
+ ${pom.artifactId}.impl.Activator
+ </Bundle-Activator>
+ <Bundle-SymbolicName>
+ ${pom.artifactId}
+ </Bundle-SymbolicName>
+ <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+ <Export-Package>
+ </Export-Package>
+ <Private-Package>
+ org.apache.felix.useradmin.*
+ </Private-Package>
+ <Export-Service>
+ org.osgi.service.useradmin.UserAdmin
+ </Export-Service>
+ <Include-Resource>
+ </Include-Resource>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>rat-maven-plugin</artifactId>
+ <configuration>
+ <excludeSubProjects>false</excludeSubProjects>
+ <useEclipseDefaultExcludes>true</useEclipseDefaultExcludes>
+ <useMavenDefaultExcludes>true</useMavenDefaultExcludes>
+ <excludes>
+ <param>doc/*</param>
+ <param>maven-eclipse.xml</param>
+ <param>.checkstyle</param>
+ <param>.externalToolBuilders/*</param>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/Base64.java b/useradmin/src/main/java/org/apache/felix/useradmin/Base64.java
new file mode 100644
index 0000000..9d0501c
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/Base64.java
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin;
+
+/**
+ * This interface represents contract for Base64 encoding.
+ *
+ *
+ * @version $Rev$ $Date$
+ */
+public interface Base64
+{
+ /**
+ * Encrypt value object must be String or byte array with Base64 algorithm.
+ * @param value String to be encoded
+ * @return encoded value with Base64
+ */
+ Object encrypt(Object value);
+
+ /**
+ * This method is decrypting encoded value with Base64.
+ * @param value encoded value.
+ * @return decrypted value.
+ */
+ Object decrypt(Object value);
+
+ /**
+ * This method is decrypting encoded value with Base64 to byte[].
+ * @param value encoded value.
+ * @return decrypted value.
+ */
+ byte[] decryptToByteArray(Object value);
+
+ /**
+ * Verifying two values if there are equal.
+ * @param value object to be verified.
+ * @param encrypted value.
+ * @return true if those 2 values are equal if not false.
+ */
+ boolean verify(Object value, Object encrypted);
+
+ /**
+ * Setting character set.
+ * @param charset Character set.
+ */
+ void setCharset(String charset);
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/CredentialAuthenticator.java b/useradmin/src/main/java/org/apache/felix/useradmin/CredentialAuthenticator.java
new file mode 100644
index 0000000..f428150
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/CredentialAuthenticator.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin;
+
+/**
+ * <p>Credential authenticator used for authenticate base on stored credentials.</p>
+ *
+ * @version $Rev$ $Date$
+ */
+public interface CredentialAuthenticator
+{
+ /**
+ * Encrypt provided credential value with one of
+ * algorithms Base64, SHA-1, etc.
+ * @param credential to be encrypted.
+ * @return encrypted value.
+ */
+ Object encryptCredential(Object credential);
+
+ /**
+ * Authenticate provided value against encrypted stored
+ * value.
+ * @param value to be check against encrypted Value.
+ * @param encryptedValue encrypted value.
+ * @return true if user is authenticated false if not.
+ */
+ boolean authenticate(Object value, Object encryptedValue);
+
+ /**
+ * This method returns Base64 encoder.
+ * @return base64 encoder.
+ */
+ Base64 getBase64();
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/MessageDigester.java b/useradmin/src/main/java/org/apache/felix/useradmin/MessageDigester.java
new file mode 100644
index 0000000..43828bb
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/MessageDigester.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin;
+
+/**
+ * <p>This interface represents MessageDigester which using MessageDigest to
+ * encrypt credential with one of algorithms SHA-1,etc. and verify hashes.</p>
+ *
+ * @version $Rev$ $Date$
+ */
+public interface MessageDigester
+{
+ /**
+ * Encrypting provided value with one of available algorithms like SHA-1.
+ *
+ * @param value to be encrypted.
+ * @param salt will be use to encrypts the value.
+ * @return encrypted value.
+ */
+ byte[] encrypt(Object value, byte[] salt);
+
+ /**
+ * <p>Verify not encoded credential against encoded one.
+ * This method is encoding provided value and compare generated hash with provided
+ * hash(digest param).</p>
+ *
+ * @param value not encoded value to be verified.
+ * @param digest encoded value (hash).
+ * @param lenghBytes length of salt byte value.
+ * @return true if hash of provided value matches digest param.
+ */
+ boolean verify(Object value, byte[] digest, int lenghBytes);
+
+ /**
+ * Generate salt used by digester to digest message.
+ * @param lengthBytes length of salt.
+ * @return salt value.
+ */
+ byte[] generateSalt(int lengthBytes);
+
+ /**
+ * Setting char set for digester.
+ * @param charset char set.
+ */
+ void setCharset(String charset);
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/UserAdminEventDispatcher.java b/useradmin/src/main/java/org/apache/felix/useradmin/UserAdminEventDispatcher.java
new file mode 100644
index 0000000..fc6504d
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/UserAdminEventDispatcher.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin;
+
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/**
+ * <p>The <tt>UserAdminEventDispatcher</tt> represents event dispatcher
+ * used for dispatching UserAdminEvent to the UserAdminListeners and
+ * EventAdmin topic.
+ * </p>
+ *
+ * @see org.osgi.serice.useradmin.UserAdminListener
+ * @version $Rev$ $Date$
+ */
+public interface UserAdminEventDispatcher
+{
+ /**
+ * Starting dispatcher thread.
+ */
+ void start();
+
+ /**
+ * Dispatching UserAdminEvent asynchronously.
+ *
+ * @param event UserAdmin event.
+ */
+ void dispatchEventAsynchronusly(UserAdminEvent userAdminEvent);
+
+ /**
+ * Closing dispatcher thread.
+ */
+ void close();
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/UserAdminRepository.java b/useradmin/src/main/java/org/apache/felix/useradmin/UserAdminRepository.java
new file mode 100644
index 0000000..5daf3a5
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/UserAdminRepository.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin;
+
+import java.util.Hashtable;
+
+/**
+ * The <tt>UserAdminRepository</tt> represents contract for
+ * UserAdmin repository which cached and persists Roles into the file.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface UserAdminRepository
+{
+ /**
+ * This method flush repository cache into to repository file.
+ */
+ void flush();
+
+ /**
+ * This method is getting UserAdmin repository cache.
+ *
+ * @return repository cache.
+ */
+ Hashtable getRepositoryCache();
+
+ /**
+ * This method is loading data from file repository into cache.
+ */
+ void load();
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/UserAdminRepositoryManager.java b/useradmin/src/main/java/org/apache/felix/useradmin/UserAdminRepositoryManager.java
new file mode 100644
index 0000000..de868ac
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/UserAdminRepositoryManager.java
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin;
+
+import org.apache.felix.useradmin.impl.UserAdminServiceImpl;
+import org.osgi.framework.Filter;
+import org.osgi.service.useradmin.Role;
+
+/**
+ * UserAdminRepository manager.
+ * Provides methods for storing roles, removing and finding
+ * from roles repository.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface UserAdminRepositoryManager
+{
+ /**
+ * Initialising roles repository manager.
+ *
+ * @param userAdmin role dependency needs to be injected.
+ */
+ void initialize(UserAdminServiceImpl userAdmin);
+
+ /**
+ * Finding role by role name.
+ *
+ * @param name role name.
+ * @return Role instance or null if can't find it.
+ */
+ Role findRoleByName(String name);
+
+ /**
+ * Finding Role by role type and property of a role.
+ *
+ * @param roleType role type User,etc.
+ * @param key key value of property.
+ * @param value property value.
+ * @return Role instance or null.
+ */
+ Object findRoleByTypeAndKeyValue(int roleType, String key, String value);
+
+ /**
+ * Find roles by filter.
+ *
+ * @param filter @see org.osgi.framework.Filter.
+ * @return array of Roles.
+ */
+ Role[] findRolesByFilter(Filter filter);
+
+ /**
+ * Saving role with specific name and type.
+ *
+ * @param name role name.
+ * @param type role type.
+ * @param userAdmin role dependency.
+ * @return role if created successfully if not null.
+ */
+ Role save(String name, int type, UserAdminServiceImpl userAdmin);
+
+ /**
+ * Remove role with provided name.
+ *
+ * @param name role name.
+ * @return removed Role if any.
+ */
+ Role remove(String name);
+
+ /**
+ * Flushing changes into the store file.
+ */
+ void flush();
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/Version.java b/useradmin/src/main/java/org/apache/felix/useradmin/Version.java
new file mode 100644
index 0000000..f89c3a0
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/Version.java
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin;
+
+/**
+ * The <tt>Version</tt> interface represents version of a role.
+ *
+ *
+ * @version $Rev$ $Date$
+ */
+public interface Version
+{
+ /**
+ * This method getting version of a role.
+ *
+ * @return version
+ */
+ long getVersion();
+
+ /**
+ * This method increasing version of a role.
+ */
+ void increaseVersion();
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/Activator.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/Activator.java
new file mode 100644
index 0000000..3486beb
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/Activator.java
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import org.apache.felix.useradmin.UserAdminRepository;
+import org.apache.felix.useradmin.UserAdminRepositoryManager;
+import org.apache.felix.useradmin.UserAdminEventDispatcher;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * <p>
+ * This <tt>Activator</tt> represents activator for UserAdmin service bundle.
+ * Its registering UserAdmin service in the ServiceRegistry and
+ * manage all its dependencies.</p>
+ *
+ * @version $Rev$ $Date$
+ */
+public class Activator implements BundleActivator
+{
+ private Logger logger;
+ private ServiceRegistration registration;
+ private UserAdminServiceImpl userAdmin;
+
+ /**
+ * @see org.osgi.framework.BundleActivator#start(BundleContext)
+ */
+ public void start(BundleContext context) throws Exception
+ {
+ //Starting logger
+ logger = new Logger(context);
+ logger.open();
+ //Creating repository of roles for UserAdmin service
+ UserAdminRepository repository = new UserAdminRepositoryImpl(logger, context);
+ //Creating manager for roles repository
+ UserAdminRepositoryManager repositoryManager = new UserAdminRepositoryManagerImpl(logger, repository);
+ //Creating dispatcher for UserAdmin events
+ UserAdminEventDispatcher eventDispatcher = new UserAdminEventDipatcherImpl(context);
+ userAdmin = new UserAdminServiceImpl(context, repositoryManager, logger, eventDispatcher);
+ repositoryManager.initialize(userAdmin);
+
+ registration = context.registerService(UserAdmin.class.getName(), userAdmin, null);
+ userAdmin.setServiceRef(registration.getReference());
+ }
+
+ /**
+ * <p>This method is unregistering UserAdmin service,closing logger and
+ * closing resources kept by UserAdmin.</p>
+ *
+ * @see org.osgi.framework.BundleActivator#stop(BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception
+ {
+ if (registration != null)
+ {
+ registration.unregister();
+ }
+ if (userAdmin != null)
+ {
+ userAdmin.destroy();
+ }
+ if (logger != null)
+ {
+ logger.close();
+ }
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/AuthorizationImpl.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/AuthorizationImpl.java
new file mode 100644
index 0000000..60c9a23
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/AuthorizationImpl.java
@@ -0,0 +1,157 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import java.util.Vector;
+
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+/**
+ * @see org.osgi.service.useradmin.Authorization
+ *
+ * @version $Rev$ $Date$
+ */
+public class AuthorizationImpl implements Authorization
+{
+ private User user;
+ private UserAdminServiceImpl userAdmin;
+ private Vector workingOnRoles;
+
+ /**
+ * <p>Construct new Authorization object with provided
+ * user and UserAdmin service implementation.<p>
+ *
+ * @param user User for who authorization can be checked
+ * @param userAdmin UserAdmin service implementation
+ */
+ public AuthorizationImpl(User user, UserAdminServiceImpl userAdmin)
+ {
+ this.user = user;
+ this.userAdmin = userAdmin;
+ this.workingOnRoles = new Vector();
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.Authorization#getName()
+ */
+ public String getName()
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("UserAdmin service is not available");
+ }
+ return user != null ? user.getName() : null;
+ }
+
+ /**
+ * Looking for all Roles implied by this Authorization object.
+ * @see org.osgi.service.useradmin.Authorization#getRoles()
+ */
+ public String[] getRoles()
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service is not available");
+ }
+
+ Role[] roles = null;
+ try
+ {
+ roles = userAdmin.getRoles(null);
+ Vector result = new Vector();
+
+ for (int i = 0; i < roles.length; i++)
+ {
+ String roleName = roles[i].getName();
+ // we don't include user.anyone role
+ // we include implied roles
+ if (hasRole(roleName) && !Role.USER_ANYONE.equals(roleName))
+ {
+ result.addElement(roleName);
+ }
+ }
+
+ if (result.size() == 0)
+ {
+ return null;
+ }
+
+ String[] res = new String[result.size()];
+ result.copyInto(res);
+ return res;
+ }
+ catch (InvalidSyntaxException e)
+ {
+ // not possible
+ }
+ return null;
+
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.Authorization#hasRole(java.lang.String)
+ */
+ public boolean hasRole(String name)
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service is not available");
+ }
+ Role role = userAdmin.getRole(name);
+ if (role != null)
+ {
+ return ((RoleImpl) role).impliedBy(this);
+ }
+ return false;
+
+ }
+
+ /**
+ * Adds working role to working on roles by this Autorization object.
+ *
+ * @param role to be added role.
+ */
+ protected void addWorkingOnRole(Role role)
+ {
+ workingOnRoles.addElement(role);
+ }
+
+ /**
+ * Removes working on role.
+ *
+ * @param role to be removed from working by this Autorization object roles.
+ */
+ protected void removeWorkingOnRole(Role role)
+ {
+ workingOnRoles.removeElement(role);
+ }
+
+ /**
+ * Check if current Autorization object is working on provided role.
+ * This check will avoid loop when Autorization is looking for imply roles.
+ *
+ * @param role Role on which Autorization object is working.
+ * @return true if this Autorization object is already working on provided role false if not.
+ */
+ protected boolean isWorkingOnRole(Role role)
+ {
+ return workingOnRoles.contains(role);
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/Base64Impl.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/Base64Impl.java
new file mode 100644
index 0000000..0b85eed
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/Base64Impl.java
@@ -0,0 +1,290 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import org.apache.felix.useradmin.Base64;
+
+/**
+ * Base64 encoding implementation.
+ * @see org.apache.felix.useradmin.Base64
+ *
+ * @version $Rev$ $Date$
+ */
+public class Base64Impl implements Base64
+{
+ private String charset;
+ /**
+ * 111111 six bit mask to get 6 bits
+ */
+ private static final int BIT6_MASK = 0x3f;
+ /**
+ * 11111111 8 bit mask to get 8 bits
+ */
+ private static final int BIT8_MASK = 0xff;
+ /**
+ * SIGN used to check if byte is signed
+ */
+ private static final int SIGN = -128;
+ /**
+ * used for keeping sign bit of signed numbers.
+ * when is added to sign number is keep 8 bit as 1.
+ */
+ private static final int SIGN256 = 256;
+
+ /**
+ * @see org.apache.felix.useradmin.Base64#encrypt(java.lang.Object)
+ */
+ public Object encrypt(Object value)
+ {
+ byte[] bytes;
+ try
+ {
+ bytes = value instanceof String ? ((String) value).getBytes(charset)
+ : (byte[]) value;
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ bytes = ((String) value).getBytes();
+ }
+
+ StringBuffer encrypted = new StringBuffer();
+
+ for (int i = 0; i < bytes.length; i += 3)
+ {
+ // every block of 3 bytes is going to 4*6 bits array of chars
+ encrypted.append(encryptBlock(bytes, i));
+ }
+ if (value instanceof String)
+ {
+ return encrypted.toString();
+ }
+ else
+ {
+ return encrypted.toString().getBytes();
+ }
+ }
+
+ /**
+ * This method is encrypting block of 3 bytes if are available if
+ * not pads '=' are added.
+ *
+ * @param bytes to be encrypted
+ * @param i index
+ * @return encrypted block represented by base64 alphabet
+ */
+ private char[] encryptBlock(byte[] bytes, int i)
+ {
+ // every block of 3 bytes is going to 4*6 bits array of chars
+ char[] base64chars = new char[4];
+ // block of 32 bits
+ int bits32 = 0;
+ // space left on byte array
+ int space = bytes.length - i - 1;
+ int blockSize = (space >= 2) ? 2 : space;
+
+ for (int k = 0; k <= blockSize; k++)
+ {
+ // if value is signed we need to keep
+ // need to keep sign bit 100000000 if byte is signed.
+ int val = (bytes[i + k] & SIGN) == 0 ? bytes[i + k] : bytes[i + k] + SIGN256;
+ // shift bits left to build one 32 bits block
+ bits32 += val << (8 * (2 - k));
+ }
+
+ for (int j = 0; j < 4; j++)
+ {
+ // shift bits to right and take 6 bits using mask
+ int bit6 = bits32 >>> (6 * (3 - j)) & BIT6_MASK;
+ base64chars[j] = (convertToBase64Alpabet(bit6));
+ if (space < 1)
+ {
+ base64chars[2] = '=';
+ }
+ if (space < 2)
+ {
+ base64chars[3] = '=';
+ }
+
+ }
+ return base64chars;
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.Base64#decrypt(java.lang.Object)
+ */
+ public Object decrypt(Object val)
+ {
+ byte[] decrypted = decryptToByteArray(val);
+
+ if (val instanceof String)
+ {
+ return convertToString(decrypted);
+ }
+ return decrypted;
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.Base64#decryptToByteArray(Object)
+ */
+ public byte[] decryptToByteArray(Object val)
+ {
+ String value;
+ try
+ {
+ value = val instanceof String ? (String) val : new String((byte[]) val, charset);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ value = new String((byte[]) val);
+ }
+
+ int pads = 0;
+ // looking for pads
+ for (int i = value.length() - 1; value.charAt(i) == '='; i--)
+ {
+ pads++;
+ }
+ // lenght of byte array
+ int length = value.length() * 6 / 8 - pads;
+ byte[] decrypted = new byte[length];
+ int rawIndex = 0;
+ for (int i = 0; i < value.length(); i += 4)
+ {
+ int block = (getValueFromBase64Alphabet(value.charAt(i)) << 18) + (getValueFromBase64Alphabet(value.charAt(i + 1)) << 12) + (getValueFromBase64Alphabet(value.charAt(i + 2)) << 6) + (getValueFromBase64Alphabet(value.charAt(i + 3)));
+ for (int j = 0; j < 3 && rawIndex + j < decrypted.length; j++)
+ {
+ decrypted[rawIndex + j] = (byte) ((block >> (8 * (2 - j))) & BIT8_MASK);
+ }
+ rawIndex += 3;
+ }
+ return decrypted;
+ }
+
+ private String convertToString(byte[] raw)
+ {
+ try
+ {
+ return new String(raw, charset);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ // log
+ return new String(raw);
+ }
+ }
+
+ /**
+ * <p>This method is converting 6 bit number to char.
+ * The number should be from 0-63 range. Then need
+ * to convert to ASCI [A-Z][a-z]{0-9}'+'/'.</p>
+ * @param number 6 bit number to be converted.
+ * @return converted 6bit number to char.
+ */
+ private char convertToBase64Alpabet(int number)
+ {
+ // 65-90
+ if (number >= 0 && number <= 25)
+ {
+ return (char) ('A' + number);
+ }
+ // 97-122
+ if (number > 25 && number <= 51)
+ {
+ return (char) ('a' + (number - 26));
+ }
+ // 47-57
+ if (number > 51 && number <= 62)
+ {
+ return (char) ('0' + (number - 52));
+ }
+
+ if (number == 62)
+ {
+ return '+';
+ }
+ if (number == 63)
+ {
+ return '/';
+ }
+
+ return '?';
+ }
+
+ /**
+ * <p>This method is converting characters from base64 to int value</p>.
+ * @param character char for which int value is taken.
+ * @return int value of provided character
+ */
+ private int getValueFromBase64Alphabet(char character)
+ {
+ if (character >= 'A' && character <= 'Z')
+ {
+ return character - 'A';
+ }
+ if (character >= 'a' && character <= 'z')
+ {
+ return character - 'a' + 26;
+ }
+ if (character >= '0' && character <= '9')
+ {
+ return character - '0' + 52;
+ }
+ if (character == '+')
+ {
+ return 62;
+ }
+ if (character == '/')
+ {
+ return 63;
+ }
+ if (character == '=')
+ {
+ return 0;
+ }
+ return -1;
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.Base64#verify(java.lang.Object, java.lang.Object)
+ */
+ public boolean verify(Object value, Object encrypted)
+ {
+ if (value instanceof String && encrypted instanceof String)
+ {
+ String encryptedValue = (String) encrypt(value);
+ return encryptedValue.equals(encrypted);
+ }
+ else if (value instanceof byte[] && encrypted instanceof byte[])
+ {
+ byte[] encryptedValue = (byte[]) encrypt(value);
+ return Arrays.equals(encryptedValue, (byte[]) encrypted);
+ }
+ return false;
+
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.Base64#setCharset(java.lang.String)
+ */
+ public void setCharset(String charset)
+ {
+ this.charset = charset;
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/CredentialAuthenticatorImpl.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/CredentialAuthenticatorImpl.java
new file mode 100644
index 0000000..4611637
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/CredentialAuthenticatorImpl.java
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import org.apache.felix.useradmin.Base64;
+import org.apache.felix.useradmin.CredentialAuthenticator;
+import org.apache.felix.useradmin.MessageDigester;
+
+/**
+ * <p>
+ * This <tt>CredentialAuthenticatorImpl</tt> class is used for
+ * authentication of credentials.
+ * It provides methods for encrypting credentials.
+ * Based on system properties it will choose between Base64 encoding or different
+ * algorithm.</p>
+ *
+ * @see org.apache.felix.useradmin.CredentialAuthenticator
+ * @see java.security.MessageDigest
+ * @see org.apache.felix.useradmin.Base64
+ * @version $Rev$ $Date$
+ */
+public class CredentialAuthenticatorImpl implements CredentialAuthenticator
+{
+ private static final String DEFAULT_CHARSET = "UTF-8";
+ private MessageDigester digester;
+ private Base64 base64;
+ private SecureRandom secureRandom;
+ private static final String SECURE_RANDOM_ALGORITHM = "SHA1PRNG";
+ private static final String SECURE_DEFAULT_ALGORITHM = "Base64";
+ private static final String SECURE_ALOGRITHM_PROP = "org.apache.felix.useradmin.algorithm";
+ private static final String SECURE_RNG_ALOGRITHM_PROP = "org.apache.felix.useradmin.rng.algorithm";
+ private static final String CHARSET_PROP = "org.apache.felix.useradmin.charset";
+ private String charset;
+ private final int DEFAULT_BYTES_LENGTH = 20;
+ private boolean useDefaultEncryption = true;
+
+ /**
+ * <p>
+ * Construct new CredentialAuthenticator.
+ * Its reading system properties about algorithm which should be used for encoding,charset,
+ * secure random number generator algorithm.
+ * Default algorithm is Base64 which could be overridden.</p>
+ */
+ public CredentialAuthenticatorImpl()
+ {
+ String algorithm = System.getProperty(SECURE_ALOGRITHM_PROP, SECURE_DEFAULT_ALGORITHM);
+ // random number generator algorithm used for generating salts.
+ String rngAlorithm = System.getProperty(SECURE_RNG_ALOGRITHM_PROP, SECURE_RANDOM_ALGORITHM);
+ this.charset = System.getProperty(CHARSET_PROP, DEFAULT_CHARSET);
+ this.base64 = new Base64Impl();
+ this.base64.setCharset(charset);
+ if (!algorithm.equals(SECURE_DEFAULT_ALGORITHM))
+ {
+ try
+ {
+ this.digester = new MessageDigesterImpl(algorithm, rngAlorithm);
+ this.digester.setCharset(charset);
+ this.secureRandom = SecureRandom.getInstance(SECURE_RANDOM_ALGORITHM);
+ this.secureRandom.setSeed(System.currentTimeMillis());
+ this.useDefaultEncryption = false;
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ // default encryption will be used
+ }
+ }
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.CredentialAuthenticator#encryptCredential(Object)
+ */
+ public Object encryptCredential(Object credential)
+ {
+ if (useDefaultEncryption)
+ {
+ return base64.encrypt(credential);
+ }
+
+ byte[] salt = digester.generateSalt(DEFAULT_BYTES_LENGTH);
+ byte[] digest = digester.encrypt(credential, salt);
+ return base64.encrypt(digest);
+
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.CredentialAuthenticator#authenticate(Object, Object)
+ */
+ public boolean authenticate(Object value, Object encryptedValue)
+ {
+ if (useDefaultEncryption)
+ {
+ return base64.verify(value, encryptedValue);
+ }
+ byte[] digest = base64.decryptToByteArray(encryptedValue);
+ return digester.verify(value, digest, DEFAULT_BYTES_LENGTH);
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.CredentialAuthenticator#getBase64()
+ */
+ public Base64 getBase64()
+ {
+ return base64;
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/GroupImpl.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/GroupImpl.java
new file mode 100644
index 0000000..2631a73
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/GroupImpl.java
@@ -0,0 +1,245 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.felix.useradmin.UserAdminRepositoryManager;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+
+/**
+ * This class represents Group role.
+ * Group is an aggregation of basic and required roles.
+ * Basic and required roles are used in the autorization phase.
+ *
+ * @see org.osgi.service.useradmin.Group
+ * @version $Rev$ $Date$
+ */
+public class GroupImpl extends UserImpl implements Group
+{
+ private static final long serialVersionUID = -6218617211170379394L;
+ private Vector members = new Vector();
+ private Vector requiredMembers = new Vector();
+ private transient Object lock = new Object();
+
+ /**
+ * Construct new Group role.
+ */
+ public GroupImpl()
+ {
+ super();
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.Group#addMember(Role)
+ */
+ public boolean addMember(Role role)
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service is not available");
+ }
+ userAdmin.checkPermission(userAdmin.getUserAdminPermission());
+ synchronized (lock)
+ {
+
+ if (!(role instanceof RoleImpl) || ((RoleImpl) role).userAdmin != userAdmin)
+ {
+ throw new IllegalArgumentException("Not correct role");
+ }
+ String name = role.getName();
+ if (members.contains(name) || requiredMembers.contains(name))
+ {
+ return false;
+ }
+ members.addElement(name);
+ increaseVersion();
+ UserAdminRepositoryManager repositoryManager = userAdmin.getRepositoryManager();
+ repositoryManager.flush();
+ return true;
+
+ }
+
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.Group#addRequiredMember(Role)
+ */
+ public boolean addRequiredMember(Role role)
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service is not available");
+ }
+ userAdmin.checkPermission(userAdmin.getUserAdminPermission());
+ synchronized (lock)
+ {
+ if (!(role instanceof RoleImpl) || ((RoleImpl) role).userAdmin != userAdmin)
+ {
+ throw new IllegalArgumentException("Not correct role");
+ }
+ String name = role.getName();
+ if (members.contains(name) || requiredMembers.contains(name))
+ {
+ return false;
+ }
+ requiredMembers.addElement(name);
+
+ increaseVersion();
+ UserAdminRepositoryManager repositoryManager = userAdmin.getRepositoryManager();
+ repositoryManager.flush();
+ return true;
+
+ }
+
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.Group#getMembers()
+ */
+ public Role[] getMembers()
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service is not available");
+ }
+ Role[] rs = new Role[members.size()];
+ if (rs.length == 0)
+ {
+ return null;
+ }
+ synchronized (lock)
+ {
+ Enumeration en = members.elements();
+ for (int i = 0; en.hasMoreElements(); i++)
+ {
+ rs[i] = userAdmin.getRole((String) en.nextElement());
+ }
+
+ return rs;
+ }
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.Group#getRequiredMembers()
+ */
+ public Role[] getRequiredMembers()
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service is not available");
+ }
+ Role[] rs = new Role[requiredMembers.size()];
+ if (rs.length == 0)
+ {
+ return null;
+ }
+ synchronized (lock)
+ {
+ Enumeration en = requiredMembers.elements();
+ for (int i = 0; en.hasMoreElements(); i++)
+ {
+ rs[i] = userAdmin.getRole((String) en.nextElement());
+ }
+ return rs;
+ }
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.Group#removeMember(Role)
+ */
+ public boolean removeMember(Role role)
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service is not available");
+ }
+ userAdmin.checkPermission(userAdmin.getUserAdminPermission());
+ if (role == null || !(role instanceof RoleImpl))
+ {
+ throw new IllegalArgumentException("Bad role");
+ }
+ String name = role.getName();
+ synchronized (lock)
+ {
+ boolean removed = members.remove(name) || requiredMembers.remove(name);
+ if (removed)
+ {
+ UserAdminRepositoryManager storeManager = userAdmin.getRepositoryManager();
+ storeManager.flush();
+ increaseVersion();
+ }
+ return removed;
+ }
+
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.Group#getType()
+ */
+ public int getType()
+ {
+ return Role.GROUP;
+ }
+
+ /**
+ * Checks if this role is implied by provided Authorization object.
+ * @see org.osgi.service.useradmin.Autorization
+ */
+ protected boolean impliedBy(AuthorizationImpl authorization)
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service is not available");
+ }
+
+ if (authorization.isWorkingOnRole(this))
+ {
+ //loop
+ return false;
+ }
+ authorization.addWorkingOnRole(this);
+ // First check that all required roles are implied.
+ synchronized (lock)
+ {
+ for (Enumeration en = requiredMembers.elements(); en.hasMoreElements();)
+ {
+ RoleImpl role = (RoleImpl) userAdmin.getRole((String) en.nextElement());
+ if (!role.impliedBy(authorization))
+ {
+ authorization.removeWorkingOnRole(this);
+ return false;
+ }
+ }
+ // Next check that at least one basic role is implied.
+ for (Enumeration en = members.elements(); en.hasMoreElements();)
+ {
+ RoleImpl role = (RoleImpl) userAdmin.getRole((String) en.nextElement());
+ if (role.impliedBy(authorization))
+ {
+ authorization.removeWorkingOnRole(this);
+ return true;
+ }
+ }
+ }
+
+ authorization.removeWorkingOnRole(this);
+ return false;
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/Logger.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/Logger.java
new file mode 100644
index 0000000..6650829
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/Logger.java
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * This <tt>Logger</tt> class represents ServiceTracker for LogService. It provides methods for logging messages. If
+ * LogServic is not available it logs to stdout.
+ *
+ * @see org.osgi.service.log.LogService
+ * @see org.osgi.util.tracker.ServiceTracker
+ * @version $Rev$ $Date$
+ */
+public class Logger extends ServiceTracker implements LogService
+{
+ /**
+ * Constructs new Logger(ServiceTracker for LogService).
+ *
+ * @param context bundle context.
+ */
+ public Logger(BundleContext context)
+ {
+ super(context, LogService.class.getName(), null);
+ }
+
+ /**
+ * @see org.osgi.service.log.LogService#log(int, java.lang.String)
+ */
+ public void log(int level, String message)
+ {
+ LogService logService = (LogService) getService();
+ if (logService != null)
+ {
+ logService.log(level, message);
+ }
+ else
+ {
+ System.err.println("[" + context.getBundle().getLocation() + ":" + level + "] " + message);
+ }
+
+ }
+
+ /**
+ * @see org.osgi.service.log.LogService#log(int, java.lang.String, java.lang.Throwable)
+ */
+ public void log(int level, String message, Throwable exception)
+ {
+ LogService logService = (LogService) getService();
+ if (logService != null)
+ {
+ logService.log(level, message, exception);
+ }
+ else
+ {
+ System.err.println("[" + context.getBundle().getLocation() + ":" + +level + "] " + message + ((exception == null) ? " " : exception.toString()));
+ }
+ }
+
+ /**
+ * @see org.osgi.service.log.LogService#log(org.osgi.framework.ServiceReference, int, java.lang.String)
+ */
+ public void log(ServiceReference ref, int level, String message)
+ {
+ LogService logService = (LogService) getService();
+ if (logService != null)
+ {
+ logService.log(ref, level, message);
+ }
+ else
+ {
+ System.err.println("[" + context.getBundle().getLocation() + ":" + ((ref == null) ? " " : (ref + ":")) + level + "] " + message);
+ }
+ }
+
+ /**
+ * @see org.osgi.service.log.LogService#log(org.osgi.framework.ServiceReference, int, java.lang.String,
+ * java.lang.Throwable)
+ */
+ public void log(ServiceReference ref, int level, String message, Throwable exception)
+ {
+ LogService logService = (LogService) getService();
+ if (logService != null)
+ {
+ logService.log(ref, level, message, exception);
+ }
+ else
+ {
+ System.err.println("[" + context.getBundle().getLocation() + ":" + ((ref == null) ? "" : (ref + ":")) + level + "] " + message + ((exception == null) ? " " : exception.toString()));
+ }
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/MessageDigesterImpl.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/MessageDigesterImpl.java
new file mode 100644
index 0000000..957fc80
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/MessageDigesterImpl.java
@@ -0,0 +1,157 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import org.apache.felix.useradmin.MessageDigester;
+
+/**
+ * <p>This class <tt>MessageDigesterImpl</tt> implements MessageDigester.
+ * Used for encrypting credentials with MessageDigest.</p>
+ *
+ * @version $Rev$ $Date$
+ */
+public class MessageDigesterImpl implements MessageDigester
+{
+ private String charset;
+ private MessageDigest digester;
+ private SecureRandom secureRandom;
+
+ /**
+ * Constructs new MessageDigester.
+ *
+ * @param algorithm name of algorithm to use.
+ * @throws NoSuchAlgorithmException
+ */
+ public MessageDigesterImpl(String algorithm, String randomGenerator) throws NoSuchAlgorithmException
+ {
+ this.digester = MessageDigest.getInstance(algorithm);
+ //RNG Random Number Generator
+ this.secureRandom = SecureRandom.getInstance(randomGenerator);
+ this.secureRandom.setSeed(System.currentTimeMillis());
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.MessageDigester#encryptCredential(java.lang.Object, byte[])
+ */
+ public synchronized byte[] encrypt(Object credential, byte[] salt)
+ {
+ byte[] ccredential;
+ try
+ {
+ ccredential = credential instanceof String ? ((String) credential).getBytes(charset)
+ : (byte[]) credential;
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ //log
+ ccredential = ((String) credential).getBytes();
+ }
+ digester.reset();
+ byte[] digest = null;
+ digester.update(salt);
+ digester.update(ccredential);
+ digest = digester.digest();
+ // perform iteration safe is to do more than 1000
+ for (int i = 0; i <= 1001; i++)
+ {
+ digester.reset();
+ digest = digester.digest(digest);
+ }
+ return concatenate(salt, digest);
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.MessageDigester#verify(java.lang.Object, byte[], int)
+ */
+ public boolean verify(Object plainCredential, byte[] digest, int lenghBytes)
+ {
+ byte[] salt = getSalt(digest, 0, lenghBytes);
+ byte[] encCredential = encrypt(plainCredential, salt);
+ return MessageDigest.isEqual(encCredential, digest);
+ }
+
+ /**
+ * Getting salt from encoded bytes.
+ *
+ * @param array digest.
+ * @param startIndexInclusive inclussive index.
+ * @param endIndexExclusive end exclusive index.
+ * @return salt byte array.
+ */
+ private byte[] getSalt(byte[] array, int startIndexInclusive, int endIndexExclusive)
+ {
+ if (array == null)
+ {
+ return null;
+ }
+ if (startIndexInclusive < 0)
+ {
+ startIndexInclusive = 0;
+ }
+ if (endIndexExclusive > array.length)
+ {
+ endIndexExclusive = array.length;
+ }
+ int newSize = endIndexExclusive - startIndexInclusive;
+ if (newSize <= 0)
+ {
+ return new byte[0];
+ }
+ byte[] subarray = new byte[newSize];
+ System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
+ return subarray;
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.MessageDigester#generateSalt(int)
+ */
+ public synchronized byte[] generateSalt(int lengthBytes)
+ {
+ byte[] salt = new byte[lengthBytes];
+ this.secureRandom.nextBytes(salt);
+
+ return salt;
+ }
+
+ /**
+ * Concatenates two arrays of bytes.
+ *
+ * @param arraya byte array.
+ * @param arrayb byte array.
+ * @return concatenated array.
+ */
+ private byte[] concatenate(byte[] arraya, byte[] arrayb)
+ {
+ byte[] result = new byte[arraya.length + arrayb.length];
+ System.arraycopy(arraya, 0, result, 0, arraya.length);
+ System.arraycopy(arrayb, 0, result, arraya.length, arrayb.length);
+ return result;
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.MessageDigester#setCharset(String)
+ */
+ public void setCharset(String charset)
+ {
+ this.charset = charset;
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleCredentials.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleCredentials.java
new file mode 100644
index 0000000..abdedea
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleCredentials.java
@@ -0,0 +1,81 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import org.osgi.service.useradmin.UserAdminPermission;
+
+/**
+ * <p>
+ * This class <tt>RoleCredentials</tt> represents role credentials hashtable. User of this class needs to have proper
+ * permissions UserAdminPermission#CHANGE_CREDENTIAL to modify credentials and UserAdminPermission#GET_CREDENTIAL to get
+ * credentials.
+ * Inherits methods from RoleProperties.
+ * </p>
+ *
+ * @see org.apache.felix.useradmin.impl.RoleProperties
+ *
+ * @version $Rev$ $Date$
+ */
+public class RoleCredentials extends RoleProperties
+{
+ private static final long serialVersionUID = 8503492916531864487L;
+
+ /**
+ * Constructs new RoleCredentials.
+ * @param role Role instance.
+ */
+ public RoleCredentials(RoleImpl role)
+ {
+ super(role);
+ }
+
+ /**
+ * The permission need to modify the credentials.
+ */
+ protected String getChangeAction()
+ {
+ return UserAdminPermission.CHANGE_CREDENTIAL;
+ }
+
+ /**
+ * Gets credential for specified key.
+ * User of this method needs to have UserAdminPermission#GET_CREDENTIAL permissions.
+ */
+ public synchronized Object get(Object key)
+ {
+ if (!role.userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service not available");
+ }
+
+ if (key instanceof String)
+ {
+ // Check that the caller are allowed to get the credential.
+ role.userAdmin.checkPermission(new UserAdminPermission((String) key, UserAdminPermission.GET_CREDENTIAL));
+ return super.get(key);
+ }
+ else
+ {
+ throw new IllegalArgumentException("The key must be a String, got " + key.getClass());
+ }
+ }
+
+ public String toString()
+ {
+ return "#Credentials#";
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleImpl.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleImpl.java
new file mode 100644
index 0000000..0451147
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleImpl.java
@@ -0,0 +1,135 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import java.io.Serializable;
+import java.util.Dictionary;
+
+import org.apache.felix.useradmin.Version;
+import org.osgi.service.useradmin.Role;
+
+/**
+ * <p>This <tt>RoleImpl</tt>class represents Role.
+ * Act as base class for different types of roles User,Group.</p>
+ *
+ * @see org.osgi.service.useradmin.Role
+ * @version $Rev$ $Date$
+ */
+public class RoleImpl implements Role, Version, Serializable
+{
+ private static final long serialVersionUID = -4157076907548034363L;
+ /**
+ * role version.
+ */
+ private long version;
+ /**
+ * role name.
+ */
+ protected String name;
+ /**
+ * UserAdmin service instance.
+ */
+ protected transient UserAdminServiceImpl userAdmin;
+ /**
+ * role properties.
+ */
+ private Dictionary properties;
+
+ /**
+ * Construct new Role.
+ */
+ public RoleImpl()
+ {
+ this.properties = new RoleProperties(this);
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.Role#getName()
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.Role#getProperties()
+ */
+ public Dictionary getProperties()
+ {
+ return properties;
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.Role#getType()
+ */
+ public int getType()
+ {
+ return Role.ROLE;
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.Version#getVersion()
+ */
+ public long getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.Version#increaseVersion()
+ */
+ public void increaseVersion()
+ {
+ version++;
+ }
+
+ /**
+ * Checks if this role is implied by provided Authorization object.
+ * @see org.osgi.service.useradmin.Autorization
+ * @param authorization Authorization instance.
+ * @return true if is implied false if not.
+ */
+ protected boolean impliedBy(AuthorizationImpl authorization)
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service is not available");
+ }
+ String rolename = authorization.getName();
+ boolean implied = (rolename != null && rolename.equals(name)) || name.equals(Role.USER_ANYONE);
+ return implied;
+
+ }
+
+ /**
+ * Setting UserAdmin.
+ * @param userAdmin UserAdmin isntance.
+ */
+ public void setUserAdmin(UserAdminServiceImpl userAdmin)
+ {
+ this.userAdmin = userAdmin;
+ }
+
+ /**
+ * Setting role name.
+ * @param name role name.
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleProperties.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleProperties.java
new file mode 100644
index 0000000..bc9ff67
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/RoleProperties.java
@@ -0,0 +1,214 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.felix.useradmin.Base64;
+import org.apache.felix.useradmin.CredentialAuthenticator;
+import org.apache.felix.useradmin.UserAdminEventDispatcher;
+import org.apache.felix.useradmin.UserAdminRepositoryManager;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminPermission;
+
+/**
+ * This class <tt>RoleProperties</tt> represents role properties.
+ * Act as a base class for different types of properties.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RoleProperties extends Hashtable
+{
+ private static final long serialVersionUID = -2989683398828827588L;
+ protected RoleImpl role;
+
+ /**
+ * Constructs new RoleProperties.
+ * @param role Role instance.
+ */
+ public RoleProperties(RoleImpl role)
+ {
+ this.role = role;
+ }
+
+ /**
+ * Clears the properties.
+ * User needs to have proper change permissions.
+ */
+ public synchronized void clear()
+ {
+ if (!role.userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service not available");
+ }
+
+ Enumeration e = keys();
+ while (e.hasMoreElements())
+ {
+ String key = (String) e.nextElement();
+ role.userAdmin.checkPermission(new UserAdminPermission(key, getChangeAction()));
+
+ }
+ super.clear();
+ role.increaseVersion();
+ UserAdminRepositoryManager repositoryManager = role.userAdmin.getRepositoryManager();
+ repositoryManager.flush();
+ UserAdminEventDispatcher eventDispatcher = role.userAdmin.getEventAdminDispatcher();
+ eventDispatcher.dispatchEventAsynchronusly(new UserAdminEvent(role.userAdmin.getServiceRef(),
+ UserAdminEvent.ROLE_CHANGED, role));
+
+ }
+
+ /**
+ * Getting property with specified key.
+ */
+ public synchronized Object get(Object key)
+ {
+ if (!role.userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service not available");
+ }
+ Object value = super.get(key);
+ if (value == null)
+ {
+ return null;
+ }
+ else
+ {
+ // only encoding properties
+ if (getChangeAction().equals(UserAdminPermission.CHANGE_PROPERTY))
+ {
+ CredentialAuthenticator authenticator = role.userAdmin.getAuthenticator();
+ if (authenticator == null)
+ {
+ return null;
+ }
+ Base64 base64 = authenticator.getBase64();
+ return base64.decrypt(value);
+ }
+ return value;
+ }
+
+ }
+
+ /**
+ * Removing properties with specified key.
+ * User of this methods needs to have proper permissions.
+ * For removing credentials UserAdminPermission#CHANGE_CREDENTIAL
+ * For removing properties UserAdminPermission#CHANGE_PROPERTY.
+ */
+ public synchronized Object remove(Object key)
+ {
+ if (!role.userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service not available");
+ }
+
+ if (key instanceof String)
+ {
+ // Check that the caller is allowed to remove the property.
+ UserAdminServiceImpl userAdmin = role.userAdmin;
+ role.userAdmin.checkPermission(new UserAdminPermission((String) key, getChangeAction()));
+ Object res = super.remove(key);
+ role.increaseVersion();
+ UserAdminRepositoryManager repositoryManager = role.userAdmin.getRepositoryManager();
+ repositoryManager.flush();
+ UserAdminEventDispatcher eventDispatcher = userAdmin.getEventAdminDispatcher();
+ eventDispatcher.dispatchEventAsynchronusly(new UserAdminEvent(userAdmin.getServiceRef(),
+ UserAdminEvent.ROLE_CHANGED, role));
+
+ return res;
+ }
+ else
+ {
+ throw new IllegalArgumentException("The key must be a String, got " + key.getClass());
+ }
+
+ }
+
+ /**
+ * <p>
+ * Putting new property key-value pair into properties.
+ * User needs to have proper change permissions.
+ * All values are encoded at least with Base64.</p>
+ */
+ public synchronized Object put(Object key, Object value)
+ {
+ if (!role.userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service not available");
+ }
+
+ if (key instanceof String)
+ {
+ if (value instanceof String || value instanceof byte[])
+ {
+ UserAdminServiceImpl userAdmin = role.userAdmin;
+ userAdmin.checkPermission(new UserAdminPermission((String) key, getChangeAction()));
+ Object res = null;
+ CredentialAuthenticator authenticator = userAdmin.getAuthenticator();
+ if (authenticator == null)
+ {
+ return null;
+ }
+ if (getChangeAction().equals(UserAdminPermission.CHANGE_PROPERTY))
+ {
+ // for properties only base64
+ Base64 base64 = authenticator.getBase64();
+ res = base64.encrypt(value);
+ super.put(key, res);
+ }
+ else
+ {
+ // for credentials using base64 or different algorithm SHA-1, etc.
+ res = authenticator.encryptCredential(value);
+ super.put(key, res);
+ }
+
+ UserAdminRepositoryManager repositoryManager = role.userAdmin.getRepositoryManager();
+ repositoryManager.flush();
+ UserAdminEventDispatcher eventDispatcher = userAdmin.getEventAdminDispatcher();
+ eventDispatcher.dispatchEventAsynchronusly(new UserAdminEvent(userAdmin.getServiceRef(),
+ UserAdminEvent.ROLE_CHANGED, role));
+ return res;
+ }
+ else
+ {
+ throw new IllegalArgumentException("The value must be of type String or byte[], got " + value.getClass());
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("The key must be a String, got " + key.getClass());
+ }
+
+ }
+
+ public String toString()
+ {
+ return "#Properties";
+ }
+
+ /**
+ * The permission need to modify the properties.
+ */
+ protected String getChangeAction()
+ {
+ return UserAdminPermission.CHANGE_PROPERTY;
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminEventDipatcherImpl.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminEventDipatcherImpl.java
new file mode 100644
index 0000000..72bc04c
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminEventDipatcherImpl.java
@@ -0,0 +1,218 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.felix.useradmin.UserAdminEventDispatcher;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminListener;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Dispatching UserAdmin events.
+ * @see org.apache.felix.useradmin.UserAdminEventDispatcher
+ *
+ * @version $Rev$ $Date$
+ */
+public class UserAdminEventDipatcherImpl extends Thread implements UserAdminEventDispatcher
+{
+ private Vector queue;
+ private ServiceTracker userAdminTrackerListener;
+ private ServiceTracker eventAdminTracker;
+ private static final String userAdminTopic = "org/osgi/service/useradmin/UserAdmin/";
+ private volatile boolean running;
+
+ /**
+ * This constructor is used to create UserAdmin event dispatcher.
+ * It creating and opening two trackers for UserAdminListener and EventAdmin service.
+ * Setting thread as a daemon.
+ * @param context bundle context
+ */
+ public UserAdminEventDipatcherImpl(BundleContext context)
+ {
+ super();
+ this.queue = new Vector();
+ this.userAdminTrackerListener = new ServiceTracker(context, UserAdminListener.class.getName(), null);
+ this.userAdminTrackerListener.open();
+ this.eventAdminTracker = new ServiceTracker(context, EventAdmin.class.getName(), null);
+ this.eventAdminTracker.open();
+ this.running = true;
+ setDaemon(true);
+ setName("UserAdminEventDispatcher-Thread");
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.impl.UserAdminEventDispatcher#start()
+ */
+ public void start()
+ {
+ super.start();
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.impl.UserAdminEventDispatcher#run()
+ */
+ public void run()
+ {
+ while (running)
+ {
+ UserAdminEvent event = take();
+ if (event != null)
+ {
+ notifyListeners(event);
+ }
+
+ }
+ }
+
+ /**
+ * Notifying UserAdminListeners about change made to roles.
+ *
+ * @param event @see org.osgi.service.useradmin.UserAdminEvent
+ */
+ private void notifyListeners(UserAdminEvent event)
+ {
+ Object[] services = userAdminTrackerListener.getServices();
+ if (services != null)
+ {
+ for (int i = 0; i < services.length; i++)
+ {
+ UserAdminListener listener = ((UserAdminListener) services[i]);
+ listener.roleChanged(event);
+ }
+ }
+ }
+
+ /**
+ * @see
+ * org.apache.felix.useradmin.impl.UserAdminEventDispatcher#dispatchEventAsynchronusly(org.osgi.service.useradmin
+ * .UserAdminEvent)
+ */
+ public synchronized void dispatchEventAsynchronusly(UserAdminEvent userAdminEvent)
+ {
+ EventAdmin eventAdmin = (EventAdmin) eventAdminTracker.getService();
+ if (eventAdmin != null)
+ {
+ Event event = createEvent(userAdminEvent);
+ eventAdmin.postEvent(event);
+ }
+ queue.add(userAdminEvent);
+ notifyAll();
+ }
+
+ /**
+ * This method is consuming event from the queue if queue is empty and dispatcher is running
+ * waiting for events.
+ * @return UserAdmin event
+ */
+ private synchronized UserAdminEvent take()
+ {
+
+ while (running && queue.isEmpty())
+ {
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ if (running)
+ {
+ UserAdminEvent event = (UserAdminEvent) queue.get(0);
+ queue.removeElementAt(0);
+ return event;
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ /**
+ * Closing UserAdminTrackers and putting running state to false.
+ * @see org.apache.felix.useradmin.impl.UserAdminEventDispatcher#close()
+ */
+ public void close()
+ {
+ userAdminTrackerListener.close();
+ running = true;
+ }
+
+ /**
+ * This method is creating OSGi event from UserAdminEvent.
+ * @param userAdminEvent event.
+ * @return OSGi event converted from UserAdmin event.
+ */
+ private Event createEvent(UserAdminEvent userAdminEvent)
+ {
+ String topic = getEventAdminTopic(userAdminEvent.getType());
+ Dictionary eventProperties = new Hashtable();
+
+ eventProperties.put(EventConstants.EVENT_TOPIC, topic);
+ eventProperties.put(EventConstants.EVENT, userAdminEvent);
+ eventProperties.put(EventConstants.TIMESTAMP, new Long(System.currentTimeMillis()));
+ eventProperties.put("role", userAdminEvent.getRole());
+ eventProperties.put("role.name", userAdminEvent.getRole().getName());
+ eventProperties.put("role.type", new Integer(userAdminEvent.getRole().getType()));
+ eventProperties.put(EventConstants.SERVICE, userAdminEvent.getServiceReference());
+ eventProperties.put(EventConstants.SERVICE_ID, userAdminEvent.getServiceReference().getProperty(
+ Constants.SERVICE_ID));
+ eventProperties.put(EventConstants.SERVICE_OBJECTCLASS, userAdminEvent.getServiceReference().getProperty(
+ Constants.OBJECTCLASS));
+ eventProperties.put(EventConstants.SERVICE_PID, userAdminEvent.getServiceReference().getProperty(
+ Constants.SERVICE_PID));
+
+ return new Event(topic, eventProperties);
+
+ }
+
+ /**
+ * This method is getting topic for specific event type.
+ * @param type role type.
+ * @return OSGi topic specific for UserAdminEvent.
+ */
+ private String getEventAdminTopic(int type)
+ {
+ String evtType = "?";
+
+ switch (type)
+ {
+ case UserAdminEvent.ROLE_CREATED:
+ evtType = "ROLE_CREATED";
+ break;
+ case UserAdminEvent.ROLE_CHANGED:
+ evtType = "ROLE_CHANGED";
+ break;
+ case UserAdminEvent.ROLE_REMOVED:
+ evtType = "ROLE_REMOVED";
+ break;
+ }
+ return userAdminTopic + evtType;
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminRepositoryImpl.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminRepositoryImpl.java
new file mode 100644
index 0000000..6a862b0
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminRepositoryImpl.java
@@ -0,0 +1,150 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Hashtable;
+
+import org.apache.felix.useradmin.UserAdminRepository;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * UserAdminRepository implementation of {@link UserAdminRepository}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class UserAdminRepositoryImpl implements UserAdminRepository
+{
+ /**
+ * UserAdmin repository cache, caching all roles during runtime.
+ */
+ private Hashtable repositoryCache;
+ /**
+ * Store file name.
+ */
+ private String repositoryFileName;
+ /**
+ * Store file.
+ */
+ private File repositoryFile;
+ /**
+ * Property pointing out the file containing the role database information.
+ */
+ private final static String DBPROP = "org.apache.felix.useradmin.db";
+ private Logger logger;
+
+ /**
+ * Constructs new UserAdminRepository.
+ * @param logger Logger instance.
+ * @param context bundle context.
+ */
+ public UserAdminRepositoryImpl(Logger logger, BundleContext context)
+ {
+ this.repositoryFileName = System.getProperty(DBPROP, "useradmin.db");
+ this.logger = logger;
+ this.repositoryFile = context.getDataFile(repositoryFileName);
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.UserAdminRepository#load()
+ */
+ public void load()
+ {
+ try
+ {
+ logger.log(LogService.LOG_DEBUG, "Loading User Admin Repository");
+
+ if (repositoryFile == null || !repositoryFile.exists())
+ {
+ repositoryFile = new File(repositoryFileName);
+ }
+
+ if (repositoryFile != null && repositoryFile.exists())
+ {
+ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(repositoryFile));
+ Object obj = ois.readObject();
+ ois.close();
+ if (obj instanceof Hashtable)
+ {
+ repositoryCache = (Hashtable) obj;
+ logger.log(LogService.LOG_INFO, "User Admin Repository loaded");
+ }
+ else
+ {
+ logger.log(LogService.LOG_ERROR, "User Admin Repository corrupted");
+ }
+ }
+ else
+ {
+ logger.log(LogService.LOG_DEBUG, "User Admin Repository not found ");
+ }
+ }
+ catch (IOException e)
+ {
+ logger.log(LogService.LOG_ERROR, "Can't load User Admin Repository", e);
+ }
+ catch (ClassNotFoundException e)
+ {
+ logger.log(LogService.LOG_ERROR, "Can't load User Admin Repository", e);
+ }
+
+ if (repositoryCache == null)
+ {
+ repositoryCache = new Hashtable();
+ }
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.UserAdminRepository#flush()
+ */
+ public void flush()
+ {
+
+ try
+ {
+ if (repositoryFile != null)
+ {
+ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(repositoryFile));
+ oos.writeObject(repositoryCache);
+ oos.close();
+ }
+ else
+ {
+ logger.log(LogService.LOG_DEBUG, "User Admin Repository not found ");
+ }
+ }
+ catch (IOException e)
+ {
+ logger.log(LogService.LOG_ERROR, "Failed to save roles", e);
+ }
+
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.UserAdminRepository#getRepositoryCache()
+ */
+ public Hashtable getRepositoryCache()
+ {
+ return repositoryCache;
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminRepositoryManagerImpl.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminRepositoryManagerImpl.java
new file mode 100644
index 0000000..3d216ba
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminRepositoryManagerImpl.java
@@ -0,0 +1,259 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.felix.useradmin.UserAdminRepository;
+import org.apache.felix.useradmin.UserAdminRepositoryManager;
+import org.apache.felix.useradmin.Version;
+import org.osgi.framework.Filter;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.Role;
+
+/**
+ * This class <tt>UserAdminRepositoryManagerImpl</tt> implements UserAdminRepositoryManager.
+ * Providing operations for saving,removing,flushing data to the repository.
+ * All public method are guarded by lock.
+ *
+ * @version $Rev$ $Date$
+ */
+public class UserAdminRepositoryManagerImpl implements UserAdminRepositoryManager
+{
+ private Logger logger;
+ private Object lock = new Object();
+ private UserAdminRepository store;
+
+ /**
+ * Constructs manager for UserAdminRepositoryManager.
+ * @param logger Logger instance.
+ * @param store backing store instance.
+ */
+ public UserAdminRepositoryManagerImpl(Logger logger, UserAdminRepository store)
+ {
+ this.logger = logger;
+ this.store = store;
+
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.UserAdminRepositoryManager#initialize(org.apache.felix.useradmin.impl.UserAdminServiceImpl)
+ */
+ public void initialize(UserAdminServiceImpl userAdmin)
+ {
+ logger.log(LogService.LOG_DEBUG, "Initializing repository manager");
+ synchronized (lock)
+ {
+ store.load();
+ Hashtable cache = store.getRepositoryCache();
+ initializePredefinedRole(cache);
+ injectDependencyToEntity(cache, userAdmin);
+ }
+
+ }
+
+ /**
+ * Initialising predefined Role Role.USER_ANYONE.
+ * @param storeCache store cache.
+ */
+ private void initializePredefinedRole(Hashtable storeCache)
+ {
+ RoleImpl role = new RoleImpl();
+ role.setName(Role.USER_ANYONE);
+ storeCache.put(Role.USER_ANYONE, role);
+ }
+
+ /**
+ * Injects UserAdmin to Role objects during the startup
+ * @param storeCache cache of the store.
+ * @param userAdmin UserAdmin instance.
+ */
+ private void injectDependencyToEntity(Hashtable storeCache, UserAdminServiceImpl userAdmin)
+ {
+ Enumeration en = storeCache.elements();
+ while (en.hasMoreElements())
+ {
+ RoleImpl role = (RoleImpl) en.nextElement();
+ role.setUserAdmin(userAdmin);
+ }
+ }
+
+ /**
+ * @see org.apache.felix.useradmin.UserAdminRepositoryManager#findRoleByName(java.lang.String)
+ */
+ public Role findRoleByName(String name)
+ {
+ if (name != null)
+ {
+ Hashtable cache = store.getRepositoryCache();
+ return (Role) cache.get(name);
+ }
+ return null;
+ }
+
+ /**
+ * @GuardedBy lock.
+ * @see org.apache.felix.useradmin.UserAdminRepositoryManager#findRoleByTypeAndKeyValue(int,
+ * java.lang.String, java.lang.String)
+ */
+ public Object findRoleByTypeAndKeyValue(int roleType, String key, String value)
+ {
+ synchronized (lock)
+ {
+ Vector temp = new Vector();
+ Hashtable cache = store.getRepositoryCache();
+ for (Enumeration en = cache.elements(); en.hasMoreElements();)
+ {
+ Role role = (Role) en.nextElement();
+ if (role.getType() == roleType)
+ {
+ Dictionary properties = role.getProperties();
+ Object val = properties.get(key);
+ if (value.equals(val))
+ {
+ temp.add(role);
+ }
+ }
+ }
+ return temp.isEmpty() || temp.size() > 1 ? null : temp.get(0);
+ }
+
+ }
+
+ /**
+ * <p>If a null filter is specified, all Role objects managed by User Admin service
+ * are returned.</p>
+ *
+ * @GuardedBy lock.
+ * @see org.apache.felix.useradmin.impl.UserAdminRepositoryManager#findRolesByFilter(org.osgi.framework.Filter)
+ */
+ public Role[] findRolesByFilter(Filter filter)
+ {
+ synchronized (lock)
+ {
+ Hashtable cache = store.getRepositoryCache();
+ Enumeration en = cache.elements();
+ if (filter == null)
+ {
+ Role[] rs = new Role[cache.size()];
+ for (int i = 0; en.hasMoreElements(); i++)
+ {
+ Role role = (Role) en.nextElement();
+ rs[i] = role;
+ }
+ return rs;
+ }
+ else
+ {
+ Vector temp = new Vector();
+ for (int i = 0; en.hasMoreElements(); i++)
+ {
+ Role role = (Role) en.nextElement();
+ if (filter.match(role.getProperties()))
+ {
+ temp.add(role);
+ }
+ }
+ Role[] rs = new Role[temp.size()];
+ temp.copyInto(rs);
+
+ return rs;
+ }
+ }
+ }
+
+ /**
+ * @GuardedBy lock.
+ * @see org.apache.felix.useradmin.impl.UserAdminRepositoryManager#save(java.lang.String, int,
+ * org.apache.felix.useradmin.impl.UserAdminServiceImpl)
+ */
+ public Role save(String name, int type, UserAdminServiceImpl userAdmin)
+ {
+ synchronized (lock)
+ {
+ Role role = (Role) findRoleByName(name);
+ if (role != null)
+ {
+ return null;
+ }
+
+ switch (type)
+ {
+ case Role.USER:
+ role = new UserImpl();
+ ((RoleImpl) role).setName(name);
+ ((RoleImpl) role).setUserAdmin(userAdmin);
+ break;
+ case Role.GROUP:
+ role = new GroupImpl();
+ ((RoleImpl) role).setName(name);
+ ((RoleImpl) role).setUserAdmin(userAdmin);
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ Version versionableRole = (Version) role;
+ versionableRole.increaseVersion();
+ Hashtable cache = store.getRepositoryCache();
+ cache.put(role.getName(), role);
+ store.flush();
+ return role;
+ }
+ }
+
+ /**
+ * @GuardedBy lock.
+ * @see org.apache.felix.useradmin.UserAdminRepositoryManager#remove(java.lang.String)
+ */
+ public Role remove(String name)
+ {
+ synchronized (lock)
+ {
+ if (name == null)
+ {
+ return null;
+ }
+ Hashtable cache = store.getRepositoryCache();
+ Role role = (Role) cache.remove(name);
+ if (role != null)
+ {
+ store.flush();
+ }
+ return role;
+ }
+
+ }
+
+ /**
+ * Flushing store cache content into the repository file.
+ * @GuardedBy lock.
+ * @see org.apache.felix.useradmin.UserAdminRepository#flush()
+ */
+ public void flush()
+ {
+ logger.log(LogService.LOG_DEBUG, "Flushing current state of repository cache into the file");
+ synchronized (lock)
+ {
+ store.flush();
+ }
+
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminServiceImpl.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminServiceImpl.java
new file mode 100644
index 0000000..e1c90cc
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserAdminServiceImpl.java
@@ -0,0 +1,300 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import org.apache.felix.useradmin.CredentialAuthenticator;
+import org.apache.felix.useradmin.UserAdminRepositoryManager;
+import org.apache.felix.useradmin.UserAdminEventDispatcher;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminPermission;
+
+/**
+ * <p>
+ * This <tt>UserAdminServiceImpl</tt> class implementing a contract UserAdmin. It represents UserAdmin service is
+ * exposed as a OSGi service in the ServiceRegistry.
+ * </p>
+ * <p>
+ * Its used to manage a database of named Role objects, which can be used for authentication and authorization purposes.
+ * This version of the User Admin service defines two types of Role objects: User and Group. Each type of role is
+ * represented by an int constant and an interface. The range of positive integers is reserved for new types of roles
+ * that may be added in the future. When defining proprietary role types, negative constant values must be used. Every
+ * role has a name and a type. A User object can be configured with credentials (e.g., a password) and properties (e.g.,
+ * a street address, phone number, etc.). A Group object represents an aggregation of User and Group objects. In other
+ * words, the members of a Group object are roles themselves. Every User Admin service manages and maintains its own
+ * namespace of Role objects, in which each Role object has a unique name.
+ * </p>
+ *
+ * @see org.osgi.service.useradmin.UserAdmin
+ * @see org.osgi.framework.ServiceFactory
+ * @see org.apache.felix.useradmin.UserAdminRepositoryManager
+ * @see org.apache.felix.useradmin.UserAdminEventDispatcher
+ *
+ * @version $Rev$ $Date$
+ */
+public class UserAdminServiceImpl implements UserAdmin, ServiceFactory
+{
+ private BundleContext bc;
+ /**
+ * This variable represents admin name permission for using UserAdmin.
+ */
+ private UserAdminPermission userAdminPermission;
+ private UserAdminEventDispatcher eventDispatcher;
+ /**
+ * This variable represents state of a service. if is alive is set to true if not false.
+ */
+ private boolean alive;
+ /**
+ * This variable represents ServicReference for this service. Its needed for sending events.
+ */
+ private ServiceReference serviceRef;
+ /**
+ * This variable represents repository manager.
+ */
+ private UserAdminRepositoryManager repositoryManager;
+ private Logger logger;
+ private CredentialAuthenticator authenticator;
+
+ /**
+ * This constructor is creating new UserAdmin service.
+ *
+ * @param bc BundleContext of a bundle which creating this service instance.
+ * @param repositoryManager repository manager.
+ * @param logger Logger instance.
+ * @param dispatcher UserAdmin event dispatcher instance.
+ */
+ public UserAdminServiceImpl(BundleContext bc, UserAdminRepositoryManager repositoryManager, Logger logger,
+ UserAdminEventDispatcher dispatcher)
+ {
+ this.bc = bc;
+ this.userAdminPermission = new UserAdminPermission(UserAdminPermission.ADMIN, null);
+ this.eventDispatcher = dispatcher;
+ this.eventDispatcher.start();
+ this.repositoryManager = repositoryManager;
+ this.logger = logger;
+ this.alive = true;
+ this.authenticator = new CredentialAuthenticatorImpl();
+
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.UserAdmin#createRole(String, int)
+ */
+ public Role createRole(String name, int type)
+ {
+ checkPermission(userAdminPermission);
+ Role role = repositoryManager.save(name, type, this);
+ if (role != null)
+ {
+ // dispatching an event about created role.
+ eventDispatcher.dispatchEventAsynchronusly(new UserAdminEvent(serviceRef, UserAdminEvent.ROLE_CREATED, role));
+ }
+ return role;
+ }
+
+ /**
+ * Checking permission with security manager. If the caller thread doesn't have permission it throwing
+ * SecurityException.
+ *
+ * @see java.lang.SecurityManager#checkPermission(java.security.Permission)
+ * @param permission
+ * UserAdminPermission for which check will e performed.
+ */
+ public void checkPermission(UserAdminPermission permission)
+ {
+ SecurityManager securityManager = System.getSecurityManager();
+ if (securityManager != null)
+ {
+ securityManager.checkPermission(permission);
+ }
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.UserAdmin#getAuthorization(User)
+ */
+ public Authorization getAuthorization(User user)
+ {
+ return new AuthorizationImpl(user, this);
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.UserAdmin#getRole(String)
+ */
+ public Role getRole(String name)
+ {
+ return repositoryManager.findRoleByName(name);
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.UserAdmin#getRoles(String)
+ * @see org.osgi.framework.Filter
+ */
+ public Role[] getRoles(String filter) throws InvalidSyntaxException
+ {
+ // osgi filter
+ Filter ofilter = null;
+ if (filter != null)
+ {
+ // creating a OSGi Filter for provided filter criteria
+ // filter is used for finding matching Roles.
+ ofilter = bc.createFilter(filter);
+ }
+
+ return repositoryManager.findRolesByFilter(ofilter);
+
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.UserAdmin#getUser(String, String)
+ */
+ public User getUser(String key, String value)
+ {
+ return (User) repositoryManager.findRoleByTypeAndKeyValue(Role.USER, key, value);
+
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.UserAdmin#removeRole(String)
+ */
+ public boolean removeRole(String name)
+ {
+ checkPermission(userAdminPermission);
+ if (Role.USER_ANYONE.equals(name))
+ {
+ return false;
+ }
+ Role role = repositoryManager.remove(name);
+ if (role != null)
+ {
+ eventDispatcher.dispatchEventAsynchronusly(new UserAdminEvent(serviceRef, UserAdminEvent.ROLE_REMOVED, role));
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @see org.osgi.framework.ServiceFactory#ungetService(Bundle, ServiceRegistration, Object)
+ */
+ public Object getService(Bundle bundle, ServiceRegistration reg)
+ {
+ return this;
+ }
+
+ /**
+ * @see org.osgi.framework.ServiceFactory#ungetService(Bundle, ServiceRegistration, Object)
+ */
+ public void ungetService(Bundle bundle, ServiceRegistration reg, Object obj)
+ {
+ // not used
+ }
+
+ /**
+ * <p>
+ * This method is closing UserAdmin resources. Should be used when UserAdmin service is unregistred. Alive flag is
+ * set to true, eventDispacther is closed and ServiceReference is set to null.
+ *</p>
+ */
+ public void destroy()
+ {
+ logger.log(LogService.LOG_DEBUG, "Closing UserAdmin service");
+ alive = false;
+ eventDispatcher.close();
+ serviceRef = null;
+ authenticator = null;
+ }
+
+ /**
+ * Checks if UserAdmin service is alive.
+ *
+ * @return true if service is alive or false if not.
+ */
+ public boolean isAlive()
+ {
+ return alive;
+ }
+
+ /**
+ * This method is used for setting ServiceReference of this service.
+ *
+ * @param serviceRef ServiceReference of this service.
+ */
+ public void setServiceRef(ServiceReference serviceRef)
+ {
+ this.serviceRef = serviceRef;
+ }
+
+ /**
+ * This method returns ServiceReference for this service needed for UserAdminEvent.
+ *
+ * @return ServiceReference for this service.
+ */
+ public ServiceReference getServiceRef()
+ {
+ return serviceRef;
+ }
+
+ /**
+ * This method returns UserAdminPermission with name admin.
+ *
+ * @return UserAdmingPermission with name admin.
+ */
+ public UserAdminPermission getUserAdminPermission()
+ {
+ return userAdminPermission;
+ }
+
+ /**
+ * This method returns UserAdminEvent dispatcher.
+ *
+ * @return UserAdminEventDispatcher
+ * @see org.apache.felix.useradmin.UserAdminEventDispatcher
+ */
+ public UserAdminEventDispatcher getEventAdminDispatcher()
+ {
+ return eventDispatcher;
+ }
+
+ /**
+ * This method returns repository manager instance.
+ *
+ * @return repository manager instance.
+ */
+ public UserAdminRepositoryManager getRepositoryManager()
+ {
+ return repositoryManager;
+ }
+
+ /**
+ * This method returns CredentialAuthenticator instance.
+ * @return CredentialAuthenticator instance.
+ */
+ public CredentialAuthenticator getAuthenticator()
+ {
+ return this.authenticator;
+ }
+}
diff --git a/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserImpl.java b/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserImpl.java
new file mode 100644
index 0000000..52d9925
--- /dev/null
+++ b/useradmin/src/main/java/org/apache/felix/useradmin/impl/UserImpl.java
@@ -0,0 +1,114 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.useradmin.impl;
+
+import java.util.Dictionary;
+
+import org.apache.felix.useradmin.CredentialAuthenticator;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+/**
+ * <p>
+ * This <tt>UserImpl</tt>class represents User role.
+ * A User can be configured with credentials, password,properties etc.<p>
+ *
+ * @see org.osgi.service.useradmin.Role
+ * @see org.osgi.service.useradmin.User
+ * @version $Rev$ $Date$
+ */
+public class UserImpl extends RoleImpl implements User
+{
+ private static final long serialVersionUID = 9207444218182653967L;
+ /**
+ * this variable represents user credentials.
+ */
+ private Dictionary credentials;
+
+ /**
+ * Constructs new User.
+ */
+ public UserImpl()
+ {
+ super();
+ this.credentials = new RoleCredentials(this);
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.User#getCredentials()
+ */
+ public Dictionary getCredentials()
+ {
+ return credentials;
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.User#hasCredential(String, Object)
+ */
+ public boolean hasCredential(String key, Object value)
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service is not available");
+ }
+ Object rvalue = credentials.get(key);
+ if (rvalue == null)
+ {
+ return false;
+ }
+ CredentialAuthenticator authenticator = userAdmin.getAuthenticator();
+ if (authenticator == null)
+ {
+ return false;
+ }
+
+ if (value instanceof String || value instanceof byte[])
+ {
+ return authenticator.authenticate(value, rvalue);
+ }
+ else
+ {
+ throw new IllegalArgumentException("value must be of type String or byte[]");
+ }
+
+ }
+
+ /**
+ * @see org.osgi.service.useradmin.User#getType()
+ */
+ public int getType()
+ {
+ return Role.USER;
+ }
+
+ /**
+ * Checks if this role is implied by provided Authorization object.
+ * @see org.osgi.service.useradmin.Autorization
+ * @param authorization Authorization instance.
+ * @return true if is implied false if not.
+ */
+ protected boolean impliedBy(AuthorizationImpl authorization)
+ {
+ if (!userAdmin.isAlive())
+ {
+ throw new IllegalStateException("User Admin Service is not available");
+ }
+ String rolename = authorization.getName();
+ boolean implied = (rolename != null && rolename.equals(name)) || name.equals(Role.USER_ANYONE);
+ return implied;
+ }
+}