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;

+    }

+}