Sonntag, 6. Dezember 2015

Programmatic integration with embedded LDAP

Tech stack

Implementation demonstrated in this blog is developed using JDeveloper – version 12.1.3.0.0

Use case

In any secured application, user and roles plays a significant role in driving UI behaviour. While you want certain functionality to be hidden from users not having certain roles/privileges, you may want to provide full access to certain types of users (typically application administrators).

Usually for an enterprise application, one goes with a specialized licenced product (like Oracle IDM) to take care of application user and role management. But when the application is in nascent stage and you don’t have a dedicated server configured with a security suite, the only way you can quickly build and test your features is leveraging the embedded LDAP which comes packaged with Oracle Weblogic Server.

This blog gives you a kick start on how to programmatically establish connection with the embedded LDAP and also provides basic APIs on searching a user and its roles.

Implementation steps

Step 1: Create an ADF application with Model and ViewController projects. Use the “Configure ADF Security…” wizard to secure the application. Opt for both authentication and authorization security.


Step 2: Model Project
a) Create IntegratedLDAPService class such that it returns singleton instance after establishing LDAP connection and implements the APIs to search user and roles.

 /**
     * Singleton method to get Service Helper instance
     * @return Singleton OID Service Helper
     */
    public static IntegratedLDAPService getInstance() {
        if (ldapServiceInstance == null) {
            ldapServiceInstance = new IntegratedLDAPService();
        }
        return ldapServiceInstance;
    }

    /**
     * Private constructor which sets up the Identity store connection
     * member variable*/
    private IntegratedLDAPService() {
        try {
            initIdStoreFactory();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Error initializing OID Factory:", e);
        }
    }

    private void initIdStoreFactory() {
        JpsContextFactory ctxFactory;
        try {
            ctxFactory = JpsContextFactory.getContextFactory();
            JpsContext ctx = ctxFactory.getContext();
            LdapIdentityStore idStoreService = (LdapIdentityStore) ctx.getServiceInstance(IdentityStoreService.class);

            ldapFactory = idStoreService.getIdmFactory();

            storeEnv.put(OIDIdentityStoreFactory.RT_USER_SEARCH_BASES, USER_BASES);
            storeEnv.put(OIDIdentityStoreFactory.RT_GROUP_SEARCH_BASES, GROUP_BASES);
            storeEnv.put(OIDIdentityStoreFactory.RT_USER_CREATE_BASES, USER_BASES);
            storeEnv.put(OIDIdentityStoreFactory.RT_GROUP_CREATE_BASES, GROUP_BASES);
            storeEnv.put(OIDIdentityStoreFactory.RT_GROUP_SELECTED_CREATE_BASE, GROUP_BASES[0]);
            storeEnv.put(OIDIdentityStoreFactory.RT_USER_SELECTED_CREATE_BASE, USER_BASES[0]);
        } catch (JpsException e) {
            e.printStackTrace();
            throw new RuntimeException("Jps Exception encountered", e);
        }
    }


Above code snippet implements singleton design to ensure that at any given time only one LDAP connection is initiated.


Above code snippet implements singleton design to ensure that at any given time only one LDAP connection is initiated.


Please note that the USER_BASES and GROUP_BASES objects depends on your domain configuration. For this sample, I am using the following values:


 /**
     * User base for embedded ldap
     */
    private final static String[] USER_BASES = { "ou=people,ou=myrealm,dc=DefaultDomain" };

    /**
     * Group base for embedded ldap
     */
    private final static String[] GROUP_BASES = { "ou=groups,ou=myrealm,dc=DefaultDomain" };


Here, “myrealm” is the default realm of the WLS and “DefaultDomain” is the default name of the domain created for my integrated WLS instance. If you have different values, then please update these variables accordingly.

The variable ldapFactory is a private variable which will be used to perform operations on the LDAP for any user/role operations as shown below:

Search User API
    public User searchUser(String userName) throws Exception {
        IdentityStore idStore = null;
        try {
            idStore = ldapFactory.getIdentityStoreInstance(storeEnv);

            return idStore.searchUser(userName);
        } catch (IMException e) {
            return null;
        } finally {
            try {
                if (idStore != null) {
                    idStore.close();
                }
            } catch (IMException e) {
                e.printStackTrace();
                throw new Exception("Error Closing Identity Store Connection during searchUser:" + userName +
                                    "in OID:" + e.getMessage(), e);
            }
        }
    }


Above API invokes the searchUser method to find and return the User object corresponding to a user name. If no result is found, it returns null.

Search Roles for User API
    public List getRolesForUser(User user) throws Exception {
        IdentityStore idStore = null;
        List eRoles = new ArrayList();

        try {
            idStore = ldapFactory.getIdentityStoreInstance(storeEnv);

            RoleManager rm = idStore.getRoleManager();
            SearchResponse resp = null;
            
            try {
                resp = rm.getGrantedRoles(user.getPrincipal(), false);
            } catch (ObjectNotFoundException onfe) {
                onfe.printStackTrace();
                return eRoles;
            }

            while (resp.hasNext()) {
                String name = resp.next().getName();
                EnterpriseRole eRole = new EnterpriseRole();
                eRole.setRoleName(name);
                eRoles.add(eRole);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("Error getting Roles for User:" + user.getUserProfile().getFirstName(), e);
        } finally {
            try {
                idStore.close();
            } catch (Exception e) {
                e.printStackTrace();
                throw new Exception("Error Closing Identity Store Connection during getting Roles for User:" +
                                    user.getDisplayName() + "in OID:", e);
            }

        }
        return eRoles;
    }
Above API finds and returns list of all the roles corresponding to the input User object.

Additionally, please ensure that the model project has imported all the following libraries for the project to get compiled:



b) Create an application module SecurityAM and generate its implementation class. Here we will write methods to be exposed for UI layer to consume. These methods will internally get the handle of IntegratedLDAPService class which will be one stop implementation of all LDAP related actions (establishing connection, searching user and roles etc.)

public class SecurityAMImpl extends ApplicationModuleImpl implements SecurityAM {

    IntegratedLDAPService ldapInstance = IntegratedLDAPService.getInstance();

    /**
     * This is the default constructor (do not remove).
     */
    public SecurityAMImpl() {
    }

    public User searchUser(String userName) {
        try {
            return ldapInstance.searchUser(userName);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
    public List getRolesForUser(User user){
        try {
            return ldapInstance.getRolesForUser(user);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}


c) Expose the AMImpl methods to client interface



Please note that I have created EnterpriseRole and EnterpriseUser as java entities to handle data for users and roles. This is done because User and Role are interfaces exposed by IDM and in order to handle these types we need simple java classes.

Step 3: UI Project
a) Create a page fragment, task flow and a jspx page such that we have following as our end result:



In above page we have a text box where you can type in a user name (like weblogic) and a command button “Find”. Also there is a listView component below which displays all the roles assigned to the searched user. Refer the attached application for exact code structure.

Please note that since the application is secured, you need to provide resource grants to pages and task flows in the jazn xml.

Since the application is secured, you can user any user defined in embedded LDAP to access it. The default user, weblogic, will also do just fine.

b) Find button action listener implementation looks like following:

    public void findButtonAL(ActionEvent actionEvent) {
        User user = (User)invokeEL("#{bindings.searchUser.execute}");
        
        if(user != null){
            setEL("#{pageFlowScope.displaySearchResult}", "true");
            setEL("#{pageFlowScope.user}", user);
            setUserRoles((List)invokeEL("#{bindings.getRolesForUser.execute}"));
        } else {
            setEL("#{pageFlowScope.displaySearchResult}", "false");
            setUserRoles(new ArrayList());
        }
    }


When you hit the find button, we are first invoking the searchUser method exposed by SecurityAM which searches the embedded LDAP for the user name. If the search is successful, the code further searches for all the roles defined for the searched user and displays them in the list view.

c) When a user is found following is the UI behaviour



You can verify the data from WLS console:



In above screenshot, you can see that the user weblogic has group membership (that is associated role) as “Administrators”.

d) When user is not found



What next?

You can extend this application and write your own code for doing all you want with user and roles in the embedded LDAP. This spans from creating, updating, deleting a user to managing the roles and memberships for particular users. In order to do that you may want to have a look at following javadocs:

1) UserManager class:
http://docs.oracle.com/cd/E12839_01/apirefs.1111/e14658/oracle/security/idm/UserManager.html

2) RolwManager class:
http://docs.oracle.com/cd/E12839_01/apirefs.1111/e14658/oracle/security/idm/RoleManager.html

Add all such APIs to IntegratedLDAPService and call them from AMImpl methods which UI layer can consume.

That’s it! You can download the complete application attached with the blog.

http://www.guardiansoftheoracleworld.com/blogs/adfman/2015/20151205_programmatic_integration_with_embedded_ldap.zip

Happy Coding!

Keine Kommentare:

Kommentar veröffentlichen