Extending the SOAP Wrappers for new commands

The SOAP Wrappers are great way to get up and running, but after a while you may have a specific use case which is supported by the authentication server but is not exposed by the wrappers themselves. In this tutorial we will extend the administration scenario capabilities by implementing a user attribute handler.

We will start off by importing the source code into our favorite IDE. Let's take a look at the project structure:

We have all of the scenarios encapsulated into their own packages (administration, authentication, provisioning, etc.). Inside the administration package we have a bean which exposes getters for the individual handlers. These handlers send commands to the server. There is also a model package which contains the individual entities such as user, domain, DIGIPASS, etc.

Our task is to create a new handler which will allow us to create custom user attributes. To do this we will perform the following:

  • Define a user attribute model, specifying all the required getters and setters
  • Creating a new package for user attributes which will contain:
    • a handler: exposing the core functionality which we wish to provide
    • command response
    • query response
  • Exposing the handler in the administration bean

Creating a model

Let's start off with creating the model file in the package com.vasco.identikey.model called UserAttribute.java. Every model will extend it's respective attribute set which is provided to us by the SOAP Client library:

package com.vasco.identikey.model;

import com.vasco.identikeyserver.identikeytypes.userattributetypes.UserAttributeAttributeSet;

public class UserAttribute extends UserAttributeAttributeSet {

}

We will have to define a few methods so that we can interact with the parent classes attribute collection (this collection actually contains all the data and all we will do is interact with it).

As always, we will create default constructors:

public UserAttribute() {
    getAttributes();
}

public UserAttribute(List<UserAttributeAttribute> userAttributeAttrSet) {
    attributes = userAttributeAttrSet;
}

Now we will define helper methods used to interact with the attribute set itself:

private Object get(UserAttributeAttributeIDEnum id) {
    for (UserAttributeAttribute uaa : getAttributes()) {
        if (uaa.getAttributeID() == id) {
            if (uaa.getAttributeOptions() != null && uaa.getAttributeOptions().isNull() != null && uaa.getAttributeOptions().isNull()) {
                return null;
            }
            return uaa.getValue();
        }
    }
    return null;
}

private void setAttribute(UserAttributeAttributeIDEnum id, UserAttributeAttribute attribute) {
    for (int i = 0; i < getAttributes().size(); ++i) {
        if (attributes.get(i).getAttributeID() == id) {
            attributes.remove(i);
            break;
        }
    }
    attributes.add(attribute);
}

private void set(UserAttributeAttributeIDEnum id, Object value) {
    UserAttributeAttribute attribute = new UserAttributeAttribute();
    attribute.setAttributeID(id);
    attribute.setValue(value);

    setAttribute(id, attribute);
}

Now we will create getters and setters to expose the fields we wish to be able to set. In order to see what fields we can set, let's look at the SOAP reference/WSDL (omitting the optional values):

UATTFLD_DOMAIN
UATTFLD_USERID
UATTFLD_ATTR_GROUP
UATTFLD_NAME
UATTFLD_VALUE

Let's implement a getter and setter for user id together:

public String getUserID() {
	return (String) get(UserAttributeAttributeIDEnum.UATTFLD_USERID);
}

public void setUserID(String value) {
	set(UserAttributeAttributeIDEnum.UATTFLD_USERID, value);
}

The rest of the attributes can be defined in a similar manner. You can find the complete model class here.

Creating a handler

To start off we will create a package called com.vasco.identikey.controller.administration.handler.userattribute and inside it a class UserAttributeHandler.java which extends AbstractCustomConfigurable:

public class UserAttributeHandler extends AbstractCustomConfigurable {
    
    private AdministrationSession m_session;

    public UserAttributeHandler(AdministrationSession session) {
        m_session = session;
    }


    public UserAttributeHandler(ConfigurationBean config, AdministrationSession session) {
        this(session);
        super.setConfigurationBean(config);
    }
}
Since we are in the administrative scenario we will have an administrative session object. The first constructor reads data from the properties file included in the wrappers while the second takes a configuration bean.

Before we move onto implementing the execute method we will create a response:

public class UserAttributeCommandResponse extends IdentikeyResponse {

    private UserAttribute m_results;

    public UserAttributeCommandResponse() {
    }

    public UserAttributeCommandResponse(UserAttribute userAttribute) {
        m_results = userAttribute;
    }

    public UserAttribute getResults() {
        return m_results;
    }
}
The response class will extend the identikey response class and contain an instance of the class.

Now we can move onto implementing the execute method inside the user attribute handler. We will be using the session in order to access the admin service in order to call the user attribute execute method. Since the user attribute execute method takes 3 parameters: session, command and parameters; we will create a wrapper around this method which will perform error handling for us.

public UserAttributeCommandResponse execute(UserAttributeCmdIDEnum cmd, UserAttribute params, HTTPHeaderParameters headerParams) {
	UserAttributeCommandResponse response;

    try {
		UserAttributeResults result = m_session.getAdminService(headerParams).userattributeExecute(m_session.getSessionID(), cmd, params);

		if ((result.getResultCodes().getReturnCodeEnum() == ReturnCodeEnum.RET_FAILURE) && (result.getResultCodes().getStatusCodeEnum() == StatusCodeEnum.STAT_ADMIN_SESSION_STOPPED)) {
			m_session.expireSession();
		}

		response = new UserAttributeCommandResponse(new UserAttribute(result.getResultAttribute().getAttributes()));
		response.setReturnCode(result.getResultCodes().getReturnCode());
		response.setStatusCode(result.getResultCodes().getStatusCode());

		if (result.getErrorStack() != null) {
			List<IdentikeyError> errorStack = new ArrayList<>();
			for (Error e : result.getErrorStack().getErrors()) {
				errorStack.add(new IdentikeyError(e.getErrorCode(), e.getErrorDesc()));
			}
			response.setErrorStack(errorStack);
		}

	} catch (Exception e) {
 		response = new UserAttributeCommandResponse();
 		response.setReturnCode(-1);
 		response.setStatusCode(-1);
 		response.addError(new IdentikeyError(-999, "Service is not available"));
	}
	return response;
}
Inside the try block we attempt to call userattributeExecute and populate our custom response class with the data contained in the user attribute result response. If an exception is thrown, we will specify that the service is not available.

Now we can write the individual commands, since they will be more or less the same, we will go ahead and implement the create call together:

public UserAttributeCommandResponse create(UserAttribute userAttribute) {
	return create(userAttribute, null);
}

public UserAttributeCommandResponse create(UserAttribute userAttribute, HTTPHeaderParameters headerParams) {
	return execute(UserAttributeCmdIDEnum.USERATTRIBUTECMD_CREATE, userAttribute, headerParams);
}
We will create 2 methods for each attribute depending on if you wish to use an administrative session which will expire or if you wish to use a service account API key. All the methods that you can implement (view, create, delete, update) can use our execute method in the same way.

If you have replicated this for the other methods, at this point you should be able to create, update, view, delete user attributes. Before we go and try it out, let's implement the query method, again before we do this we have to implement the response class first.

public class UserAttributeQueryResponse extends IdentikeyResponse {

    private List<UserAttribute> m_results;
    private int resultCount;

    public UserAttributeQueryResponse() {
    }

    public UserAttributeQueryResponse(List<UserAttribute> userAttributeList) {
        m_results = userAttributeList;
    }

    public List<UserAttribute> getResults() {
        return m_results;
    }

    public void setResultCount(int resultCount) {
        this.resultCount = resultCount;
    }

    public int getResultCount() {
        return resultCount;
    }
}
Again our query response will extend the identikey response class, it will contain a list of results as well as the result count.

Now we can go ahead and implement our query method:

public UserAttributeQueryResponse query(UserAttribute params, UserAttributeFieldSet fieldSet, UserAttributeQueryOptions userAttributeQueryOptions, HTTPHeaderParameters headerParams) {
        UserAttributeQueryResponse response;
        try {
            UserAttributeQueryResults result = m_session.getAdminService(headerParams).userattributeQuery(m_session.getSessionID(), params, fieldSet, userAttributeQueryOptions);

            if ((result.getResultCodes().getReturnCodeEnum() == ReturnCodeEnum.RET_FAILURE)
                    && (result.getResultCodes().getStatusCodeEnum() == StatusCodeEnum.STAT_ADMIN_SESSION_STOPPED)) {
                m_session.expireSession();
            }

            List<UserAttribute> userList = null;
            if (result.getResultAttribute() != null) {
                userList = new ArrayList<>();
                for (UserAttributeAttributeSet uaa : result.getResultAttribute().getAttributeList()) {
                    userList.add(new UserAttribute(uaa.getAttributes()));
                }
            }

            response = new UserAttributeQueryResponse(userList);
            response.setReturnCode(result.getResultCodes().getReturnCode());
            response.setReturnCode(result.getResultCodes().getReturnCode());
            response.setResultCount(result.getResultCount());

            if (result.getErrorStack() != null) {
                List<IdentikeyError> errorStack = new ArrayList<>();
                for (Error e : result.getErrorStack().getErrors()) {
                    errorStack.add(new IdentikeyError(e.getErrorCode(), e.getErrorDesc()));
                }
                response.setErrorStack(errorStack);
            }

        } catch (Exception e) {
            response = new UserAttributeQueryResponse();
            response.setReturnCode(-1);
            response.setStatusCode(-1);
            response.addError(new IdentikeyError(-999, "Service is not available"));
        }
        return response;
    }
Similar to our execute method, we will the SOAP Client's query method and populate our custom response with the results. If an exception occurs, we will catch the exception and return an error stating that the service is not available.

The entire user attribute handler class can be found here. The respective user attribute command response and user attribute query response can also be found on gitlab.

Extending the AdministrationBean

So we've created the model class as well as implemented a handler for different commands related to user attributes, to come full circle we need to expose this handler. Let's open up the class AdministrationBean.java in the com.vasco.identikey.controller.administration package and append the following getter:

public UserAttributeHandler getUserAttributeHandler() {
	if (getConfigurationBean() != null) {
		return new UserAttributeHandler(getConfigurationBean(), m_session);
	}
	return new UserAttributeHandler(m_session);
}

Now all you need to do is compile the SOAP Wrappers and you are good to go!

Conclusion

The wrappers provide a quick way to get started and are easy to extend in case that they do not provide the functionality that you need. If you want to extend the SOAP Wrappers for any other command (ex. working with policies, client components, etc.), you need to:

  1. create a model
  2. create a handler
  3. expose the handler
Show Comments