org.forgerock.openam.forgerockrest.IdentityResource.java Source code

Java tutorial

Introduction

Here is the source code for org.forgerock.openam.forgerockrest.IdentityResource.java

Source

/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions copyright [year] [name of copyright owner]".
 *
 * Copyright 2012-2015 ForgeRock AS. 
 */
package org.forgerock.openam.forgerockrest;

import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.sun.identity.sm.SMSException;
import com.sun.identity.shared.encode.Hash;
import org.forgerock.openam.cts.CTSPersistentStore;
import org.forgerock.openam.cts.api.TokenType;
import org.forgerock.openam.cts.api.fields.CoreTokenField;
import org.forgerock.openam.cts.exceptions.CoreTokenException;
import org.forgerock.openam.cts.exceptions.DeleteFailedException;
import org.apache.commons.lang.RandomStringUtils;

import com.iplanet.am.util.SystemProperties;
import com.sun.identity.idm.AMIdentity;
import com.sun.identity.idm.IdRepoException;
import com.sun.identity.idm.IdType;
import com.sun.identity.idsvcs.AccessDenied;
import com.sun.identity.idsvcs.CreateResponse;
import com.sun.identity.idsvcs.DeleteResponse;
import com.sun.identity.idsvcs.DuplicateObject;
import com.sun.identity.idsvcs.GeneralFailure;
import com.sun.identity.idsvcs.IdentityDetails;
import com.sun.identity.idsvcs.NeedMoreCredentials;
import com.sun.identity.idsvcs.ObjectNotFound;
import com.sun.identity.idsvcs.Token;
import com.sun.identity.idsvcs.UpdateResponse;
import com.sun.identity.idsvcs.TokenExpired;
import com.sun.identity.idsvcs.Attribute;
import com.sun.identity.sm.ServiceConfig;
import com.sun.identity.sm.ServiceConfigManager;
import com.sun.identity.sm.ServiceNotFoundException;
import org.forgerock.json.fluent.JsonValue;
import static org.forgerock.json.fluent.JsonValue.*;
import org.forgerock.json.fluent.JsonValueException;
import org.forgerock.json.resource.*;

import com.iplanet.sso.SSOToken;
import com.iplanet.sso.SSOException;
import com.iplanet.sso.SSOTokenManager;

import com.sun.identity.idsvcs.opensso.IdentityServicesImpl;
import static org.forgerock.openam.forgerockrest.RestUtils.getCookieFromServerContext;
import static org.forgerock.openam.forgerockrest.RestUtils.isAdmin;

import org.forgerock.json.resource.servlet.HttpContext;
import org.forgerock.openam.guice.InjectorHolder;
import org.forgerock.openam.services.RestSecurity;
import org.forgerock.openam.services.email.MailServer;
import org.forgerock.openam.services.email.MailServerImpl;
import org.forgerock.openam.utils.TimeUtils;

/**
 * A simple {@code Map} based collection resource provider.
 */
public final class IdentityResource implements CollectionResourceProvider {
    // TODO: filters, sorting, paged results.

    private static final String AM_ENCRYPTION_PWD = "am.encryption.pwd";

    private final List<Attribute> idSvcsAttrList;
    private String realm;
    private String userType;

    private ServiceConfigManager mailmgr;
    private ServiceConfig mailscm;
    Map<String, HashSet<String>> mailattrs;

    static private final CTSPersistentStore cts = InjectorHolder.getInstance(CTSPersistentStore.class);

    final static String MAIL_IMPL_CLASS = "forgerockMailServerImplClassName";
    final static String MAIL_SUBJECT = "forgerockEmailServiceSMTPSubject";
    final static String MAIL_MESSAGE = "forgerockEmailServiceSMTPMessage";

    final static private String UNIVERSAL_ID = "universalid";
    final static private String MAIL = "mail";
    final static String UNIVERSAL_ID_ABBREV = "uid";
    final static String USERNAME = "username";
    final static String EMAIL = "email";
    final static String TOKEN_ID = "tokenId";
    final static String CONFIRMATION_ID = "confirmationId";
    final static String CURRENT_PASSWORD = "currentpassword";
    private static final String USER_PASSWORD = "userpassword";

    private RestSecurity restSecurity = null;

    /**
     * Creates a backend
     */
    public IdentityResource(String userType, String realm) {
        String[] userval = { userType };
        String[] realmval = { realm };
        this.realm = realm;
        this.userType = userType;
        idSvcsAttrList = new ArrayList();
        idSvcsAttrList.add(new Attribute("objecttype", userval));
        idSvcsAttrList.add(new Attribute("realm", realmval));
        restSecurity = getRestSecurity(realm);
    }

    // Constructor used for testing...
    public IdentityResource(String userType, String realm, ServiceConfigManager mailmgr, ServiceConfig mailscm,
            RestSecurity restSecurity) {
        String[] userval = { userType };
        String[] realmval = { realm };
        this.realm = realm;
        this.userType = userType;
        idSvcsAttrList = new ArrayList();
        idSvcsAttrList.add(new Attribute("objecttype", userval));
        idSvcsAttrList.add(new Attribute("realm", realmval));
        this.mailmgr = mailmgr;
        this.mailscm = mailscm;
    }

    private static final Map<String, RestSecurity> REALM_REST_SECURITY_MAP = new ConcurrentHashMap<String, RestSecurity>();

    /**
     * Gets the user id from the session provided in the server context
     *
     * @param context Current Server Context
     * @param request Request from client to retrieve id
     * @param handler Result handler
     */
    private void idFromSession(final ServerContext context, final ActionRequest request,
            final ResultHandler<JsonValue> handler) {

        JsonValue result = new JsonValue(new LinkedHashMap<String, Object>(1));
        SSOToken ssotok;
        AMIdentity amIdentity;

        try {
            SSOTokenManager mgr = SSOTokenManager.getInstance();
            ssotok = mgr.createSSOToken(getCookieFromServerContext(context));
            amIdentity = new AMIdentity(ssotok);

            // build resource
            result.put("id", amIdentity.getName());
            result.put("realm", com.sun.identity.sm.DNMapper.orgNameToRealmName(amIdentity.getRealm()));
            result.put("dn", amIdentity.getUniversalId());
            handler.handleResult(result);

        } catch (SSOException e) {
            RestDispatcher.debug.error("IdentityResource.idFromSession() :: Cannot retrieve SSO Token: " + e);
            handler.handleError(new ForbiddenException("SSO Token cannot be retrieved.", e));
        } catch (IdRepoException ex) {
            RestDispatcher.debug.error("IdentityResource.idFromSession() :: Cannot retrieve user from IdRepo" + ex);
            handler.handleError(new ForbiddenException("Cannot retrieve id from session.", ex));
        }
    }

    /**
     * Generates a secure hash to use as token ID
     * @param resource string that will be used to create random hash
     * @return random string
     */
    static private String generateTokenID(String resource) {
        if (resource == null || resource.isEmpty()) {
            return null;
        }
        return Hash.hash(resource + RandomStringUtils.randomAlphanumeric(32));
    }

    /**
     * Generates a CTS REST Token, including realm information in its {@code CoreTokenField.STRING_ONE} field.
     *
     * @param resource The resource for which the tokenID will be generated
     * @param userId The user's ID, associated with the token
     * @param tokenLifeTimeSeconds Length of time from now in second for the token to remain valid
     * @param realmName The name of the realm in which this token is valid
     * @return the generated CTS REST token
     */
    private org.forgerock.openam.cts.api.tokens.Token generateToken(String resource, String userId,
            Long tokenLifeTimeSeconds, String realmName) {
        Calendar ttl = Calendar.getInstance();
        org.forgerock.openam.cts.api.tokens.Token ctsToken = new org.forgerock.openam.cts.api.tokens.Token(
                generateTokenID(resource), TokenType.REST);
        if (userId != null && !userId.isEmpty()) {
            ctsToken.setUserId(userId);
        }
        ctsToken.setAttribute(CoreTokenField.STRING_ONE, realmName);

        ttl.setTimeInMillis(ttl.getTimeInMillis() + (tokenLifeTimeSeconds * 1000));
        ctsToken.setExpiryTimestamp(ttl);
        return ctsToken;
    }

    /**
     * This method will create a confirmation email that contains a {@link org.forgerock.openam.cts.api.tokens.Token},
     * confirmationId and email that was provided in the request.
     * @param context Current Server Context
     * @param request Request from client to retrieve id
     * @param handler Result handler
     */
    private void createRegistrationEmail(final ServerContext context, final ActionRequest request,
            final ResultHandler<JsonValue> handler) {

        JsonValue result = new JsonValue(new LinkedHashMap<String, Object>(1));
        final JsonValue jVal = request.getContent();
        String emailAddress = null;
        String confirmationLink;
        String tokenID;

        try {

            if (restSecurity == null) {
                RestDispatcher.debug.warning("IdentityResource.createRegistrationEmail(): "
                        + "Rest Security not created. restSecurity = " + restSecurity);
                throw new NotFoundException("Rest Security Service not created");
            }
            if (!restSecurity.isSelfRegistration()) {
                RestDispatcher.debug
                        .warning("IdentityResource.createRegistrationEmail(): Self-Registration set to :"
                                + restSecurity.isSelfRegistration());
                throw new NotFoundException("Self Registration is not accessible.");
            }
            // Get full deployment URL
            HttpContext header = null;
            header = context.asContext(HttpContext.class);
            StringBuilder deploymentURL = RestUtils.getFullDeploymentURI(header.getPath());

            // Get the email address provided from registration page
            emailAddress = jVal.get(EMAIL).asString();
            if (isNullOrEmpty(emailAddress)) {
                throw new BadRequestException("Email not provided");
            }

            String subject = jVal.get("subject").asString();
            String message = jVal.get("message").asString();

            // Retrieve email registration token life time
            Long tokenLifeTime = restSecurity.getSelfRegTLT();

            // Create CTS Token
            org.forgerock.openam.cts.api.tokens.Token ctsToken = generateToken(emailAddress, "anonymous",
                    tokenLifeTime, realm);

            // Store token in datastore
            cts.create(ctsToken);
            tokenID = ctsToken.getTokenId();
            // Create confirmationId
            String confirmationId = Hash.hash(tokenID + emailAddress + SystemProperties.get(AM_ENCRYPTION_PWD));

            // Build Confirmation URL
            String confURL = restSecurity.getSelfRegistrationConfirmationUrl();
            StringBuilder confURLBuilder = new StringBuilder(100);
            if (isNullOrEmpty(confURL)) {
                confURLBuilder.append(deploymentURL.append("/json/confirmation/register").toString());
            } else {
                confURLBuilder.append(confURL);
            }

            confirmationLink = confURLBuilder.append("?confirmationId=").append(requestParamEncode(confirmationId))
                    .append("&email=").append(requestParamEncode(emailAddress)).append("&tokenId=")
                    .append(requestParamEncode(tokenID)).toString();

            if (RestDispatcher.debug.messageEnabled()) {
                RestDispatcher.debug
                        .message("IdentityResource.createRegistrationEmail(): sending confirmationLink of "
                                + confirmationLink);
            }

            // Send Registration
            sendNotification(emailAddress, subject, message, confirmationLink);
            handler.handleResult(result);
        } catch (BadRequestException be) {
            RestDispatcher.debug.error("IdentityResource.createRegistrationEmail: Cannot send email to : "
                    + emailAddress + be.getMessage());
            handler.handleError(be);
        } catch (NotFoundException nfe) {
            RestDispatcher.debug.error("IdentityResource.createRegistrationEmail: Cannot send email to : "
                    + emailAddress + nfe.getMessage());
            handler.handleError(nfe);
        } catch (Exception e) {
            RestDispatcher.debug.error("IdentityResource.createRegistrationEmail: Cannot send email to : "
                    + emailAddress + e.getMessage());
            handler.handleError(new NotFoundException("Email not sent"));
        }
    }

    /**
     * Sends email notification to end user
     * @param to Resource receiving notification
     * @param subject Notification subject
     * @param message Notification Message
     * @param confirmationLink Confirmation Link to be sent
     * @throws Exception when message cannot be sent
     */
    private void sendNotification(String to, String subject, String message, String confirmationLink)
            throws Exception {

        try {
            mailmgr = new ServiceConfigManager(RestUtils.getToken(), MailServerImpl.SERVICE_NAME,
                    MailServerImpl.SERVICE_VERSION);
            mailscm = mailmgr.getOrganizationConfig(realm, null);
            mailattrs = mailscm.getAttributes();

        } catch (SMSException smse) {
            RestDispatcher.debug.error("IdentityResource.sendNotification() :: Cannot create service "
                    + MailServerImpl.SERVICE_NAME + smse);
            throw new InternalServerErrorException("Cannot create the service: " + MailServerImpl.SERVICE_NAME,
                    smse);

        } catch (SSOException ssoe) {
            RestDispatcher.debug.error("IdentityResource.sendNotification() :: Invalid SSOToken " + ssoe);
            throw new InternalServerErrorException("Cannot create the service: " + MailServerImpl.SERVICE_NAME,
                    ssoe);
        }

        if (mailattrs == null || mailattrs.isEmpty()) {
            RestDispatcher.debug.error("IdentityResource.sendNotification() :: no attrs set" + mailattrs);
            throw new NotFoundException("No service Config Manager found for realm " + realm);
        }

        // Get MailServer Implementation class
        String attr = mailattrs.get(MAIL_IMPL_CLASS).iterator().next();
        MailServer mailServer = (MailServer) Class.forName(attr).getDeclaredConstructor(String.class)
                .newInstance(realm);

        try {
            // Check if subject has not  been included
            if (isNullOrEmpty(subject)) {
                // Use default email service subject
                subject = mailattrs.get(MAIL_SUBJECT).iterator().next();
            }
        } catch (Exception e) {
            RestDispatcher.debug
                    .warning("IdentityResource.sendNotification() :: no subject found" + e.getMessage());
            subject = "";
        }
        try {
            // Check if Custom Message has been included
            if (isNullOrEmpty(message)) {
                // Use default email service message
                message = mailattrs.get(MAIL_MESSAGE).iterator().next();
            }
            message = message + System.getProperty("line.separator") + confirmationLink;
        } catch (Exception e) {
            RestDispatcher.debug
                    .warning("IdentityResource.sendNotification() :: no message found" + e.getMessage());
            message = confirmationLink;
        }
        // Send the emails via the implementation class
        mailServer.sendEmail(to, subject, message);
    }

    /**
     * Will validate confirmationId is correct
     * @param context Current Server Context
     * @param request Request from client to confirm registration
     * @param handler Result handler
     */
    private void confirmRegistration(final ServerContext context, final ActionRequest request,
            final ResultHandler<JsonValue> handler, final String realm) {
        final String METHOD = "IdentityResource.confirmationIdCheck";
        final JsonValue jVal = request.getContent();
        String tokenID;
        String confirmationId;
        String email = null;
        String username = null;
        //email or username value used to create confirmationId
        String hashComponent = null;
        String hashComponentAttr = null;
        JsonValue result = new JsonValue(new LinkedHashMap<String, Object>(1));

        try {
            tokenID = jVal.get(TOKEN_ID).asString();
            confirmationId = jVal.get(CONFIRMATION_ID).asString();
            email = jVal.get(EMAIL).asString();
            username = jVal.get(USERNAME).asString();

            if (isNullOrEmpty(confirmationId)) {
                throw new BadRequestException("confirmationId not provided");
            }
            if (isNullOrEmpty(email) && !isNullOrEmpty(username)) {
                hashComponent = username;
                hashComponentAttr = USERNAME;
            }
            if (!isNullOrEmpty(email) && isNullOrEmpty(username)) {
                hashComponent = email;
                hashComponentAttr = EMAIL;
            }
            if (isNullOrEmpty(hashComponent)) {
                throw new BadRequestException("Required information not provided");
            }
            if (isNullOrEmpty(tokenID)) {
                throw new BadRequestException("tokenId not provided");
            }

            validateToken(tokenID, realm, email, confirmationId);

            // build resource
            result.put(hashComponentAttr, hashComponent);
            result.put(TOKEN_ID, tokenID);
            result.put(CONFIRMATION_ID, confirmationId);
            handler.handleResult(result);

        } catch (BadRequestException be) {
            RestDispatcher.debug
                    .error(METHOD + ": Cannot confirm registration/forgotPassword for : " + hashComponent, be);
            handler.handleError(be);
        } catch (Exception e) {
            RestDispatcher.debug
                    .error(METHOD + ": Cannot confirm registration/forgotPassword for : " + hashComponent, e);
            handler.handleError(new NotFoundException(e.getMessage()));
        }
    }

    /**
     * Validates a provided token against a selection of criteria to ensure that it's valid for the given
     * realm. This function is the validation equiv. of
     * {@link IdentityResource#generateToken(String, String, Long, String)}.
     *
     * @param tokenID The token ID to retrieve from the store, against which to perform validation
     * @param realm The realm under which the current request is being made, must match the realm the token was
     *              generated by
     * @param hashComponent The hash component used to created the confirmationId
     * @param confirmationId The confirmationId
     * @throws NotFoundException If the token doesn't exist in the store
     * @throws CoreTokenException If there were unexpected issues communicating with the CTS
     * @throws BadRequestException If the realm or confirmationId were invalid for the token retrieved
     */
    private void validateToken(String tokenID, String realm, String hashComponent, String confirmationId)
            throws NotFoundException, CoreTokenException, BadRequestException {

        //check expiry
        org.forgerock.openam.cts.api.tokens.Token ctsToken = cts.read(tokenID);

        if (ctsToken == null || TimeUtils.toUnixTime(ctsToken.getExpiryTimestamp()) < TimeUtils.currentUnixTime()) {
            throw new NotFoundException("Cannot find tokenID: " + tokenID);
        }

        // check confirmationId
        if (!confirmationId
                .equalsIgnoreCase(Hash.hash(tokenID + hashComponent + SystemProperties.get(AM_ENCRYPTION_PWD)))) {
            RestDispatcher.debug
                    .error("IdentityResource.confirmRegistration: Invalid confirmationId : " + confirmationId);
            throw new BadRequestException("Invalid confirmationId", null);
        }

        //check realm
        if (!realm.equals(ctsToken.getValue(CoreTokenField.STRING_ONE))) {
            RestDispatcher.debug.error("IdentityResource.confirmRegistration: Invalid realm : " + realm);
            throw new BadRequestException("Invalid realm", null);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void actionCollection(final ServerContext context, final ActionRequest request,
            final ResultHandler<JsonValue> handler) {

        RestSecurity restSecurity = getRestSecurity(realm);

        final String action = request.getAction();
        if (action.equalsIgnoreCase("idFromSession")) {
            idFromSession(context, request, handler);
        } else if (action.equalsIgnoreCase("register")) {
            createRegistrationEmail(context, request, handler);
        } else if (action.equalsIgnoreCase("confirm")) {
            confirmRegistration(context, request, handler, realm);
        } else if (action.equalsIgnoreCase("anonymousCreate")) {
            anonymousCreate(context, request, handler, restSecurity);
        } else if (action.equalsIgnoreCase("forgotPassword")) {
            generateNewPasswordEmail(context, request, handler);
        } else if (action.equalsIgnoreCase("forgotPasswordReset")) {
            anonymousUpdate(context, request, handler);
        } else { // for now this is the only case coming in, so fail if otherwise
            final ResourceException e = new NotSupportedException(
                    "Actions are not supported for resource instances");
            handler.handleError(e);
        }
    }

    /**
     * Uses an amAdmin SSOtoken to create an AMIdentity from the UID provided and checks
     * whether the AMIdentity in context is active/inactive
     * @param uid the universal identifier of the user
     * @return true is the user is active;false otherwise
     * @throws NotFoundException invalid SSOToken, invalid UID
     */
    private boolean isUserActive(String uid) throws NotFoundException {
        try {
            AMIdentity userIdentity = new AMIdentity(RestUtils.getToken(), uid);
            return userIdentity.isActive();
        } catch (IdRepoException idr) {
            RestDispatcher.debug
                    .error("IdentityResource.isUserActive(): Invalid UID: " + uid + " Exception " + idr);
            throw new NotFoundException("Invalid UID, could not retrived " + uid);
        } catch (SSOException ssoe) {
            RestDispatcher.debug.error("IdentityResource.isUserActive(): Invalid SSOToken" + " Exception " + ssoe);
            throw new NotFoundException("Invalid SSOToken " + ssoe.getMessage());
        }
    }

    private void generateNewPasswordEmail(final ServerContext context, final ActionRequest request,
            final ResultHandler<JsonValue> handler) {
        JsonValue result = new JsonValue(new LinkedHashMap<String, Object>(1));
        final JsonValue jsonBody = request.getContent();
        try {

            // Check to make sure forgotPassword enabled
            if (restSecurity == null) {
                RestDispatcher.debug.warning("IdentityResource.generateNewPasswordEmail(): "
                        + "Rest Security not created. restSecurity = " + restSecurity);
                throw new NotFoundException("Rest Security Service not created");
            }
            if (!restSecurity.isForgotPassword()) {
                RestDispatcher.debug
                        .warning("IdentityResource.generateNewPasswordEmail(): Forgot Password set to : "
                                + restSecurity.isForgotPassword());
                throw new NotFoundException("Forgot password is not accessible.");
            }

            // Generate Admin Token
            Token adminToken = new Token();
            adminToken.setId(RestUtils.getToken().getTokenID().toString());

            List<Attribute> searchAttributes = getIdentityServicesAttributes(realm);
            searchAttributes.add(getAttributeFromRequest(jsonBody));

            IdentityServicesImpl idsvc = new IdentityServicesImpl();
            List searchResults = idsvc.search(null, searchAttributes, adminToken);

            //only proceed if there is exactly one match
            if (searchResults.size() == 1) {
                String username = (String) searchResults.get(0);

                IdentityDetails identityDetails;
                identityDetails = idsvc.read(username, getIdentityServicesAttributes(realm), adminToken);
                String email = null;
                String uid = null;
                Attribute[] attrs = identityDetails.getAttributes();
                for (Attribute attribute : attrs) {
                    String attributeName = attribute.getName();
                    if (MAIL.equalsIgnoreCase(attributeName)) {
                        email = attribute.getValues()[0];
                    } else if (UNIVERSAL_ID.equalsIgnoreCase(attributeName)) {
                        uid = attribute.getValues()[0];
                    }
                }
                // Check to see if user is Active/Inactive
                if (!isUserActive(uid)) {
                    throw new ForbiddenException("Request is forbidden for this user");
                }
                // Check if email is provided
                if (email == null || email.isEmpty()) {
                    throw new InternalServerErrorException("No email provided in profile.");
                }

                // Get full deployment URL
                HttpContext header;
                header = context.asContext(HttpContext.class);
                StringBuilder deploymentURL = RestUtils.getFullDeploymentURI(header.getPath());

                String subject = jsonBody.get("subject").asString();
                String message = jsonBody.get("message").asString();

                // Retrieve email registration token life time
                if (restSecurity == null) {
                    RestDispatcher.debug.warning("IdentityResource.generateNewPasswordEmail(): "
                            + "Rest Security not created. restSecurity = " + restSecurity);
                    throw new NotFoundException("Rest Security Service not created");
                }
                Long tokenLifeTime = restSecurity.getForgotPassTLT();

                // Generate Token
                org.forgerock.openam.cts.api.tokens.Token ctsToken = generateToken(email, username, tokenLifeTime,
                        realm);

                // Store token in datastore
                cts.create(ctsToken);

                // Create confirmationId
                String confirmationId = Hash
                        .hash(ctsToken.getTokenId() + username + SystemProperties.get(AM_ENCRYPTION_PWD));

                String confirmationLink;
                // Build Confirmation URL
                String confURL = restSecurity.getForgotPasswordConfirmationUrl();
                StringBuilder confURLBuilder = new StringBuilder(100);
                if (confURL == null || confURL.isEmpty()) {
                    confURLBuilder.append(deploymentURL.append("/json/confirmation/forgotPassword").toString());
                } else {
                    confURLBuilder.append(confURL);
                }

                confirmationLink = confURLBuilder.append("?confirmationId=")
                        .append(requestParamEncode(confirmationId)).append("&tokenId=")
                        .append(requestParamEncode(ctsToken.getTokenId())).append("&username=")
                        .append(requestParamEncode(username)).toString();

                // Send Registration
                sendNotification(email, subject, message, confirmationLink);
            }
            handler.handleResult(result);
        } catch (ResourceException re) {
            // Service not available, Neither or both Username/Email provided, User inactive
            RestDispatcher.debug.error(re.getMessage(), re);
            handler.handleError(re);
        } catch (ObjectNotFound onf) {
            // User not found
            RestDispatcher.debug.error("Could not find user", onf);
            handler.handleError(ResourceException.getException(ResourceException.NOT_FOUND, "User not found", onf));
        } catch (Exception e) {
            // Intentional - all other errors are considered Internal Error.
            RestDispatcher.debug.error("Internal error", e);
            handler.handleError(
                    ResourceException.getException(ResourceException.INTERNAL_ERROR, "Failed to send mail", e));
        }
    }

    private Attribute getAttributeFromRequest(JsonValue jsonBody) throws BadRequestException {
        String username = jsonBody.get(USERNAME).asString();
        String email = jsonBody.get(EMAIL).asString();

        if (username != null && email != null) {
            throw new BadRequestException("Both username and email specified - only one allowed in request.");
        }

        if (username != null && !username.isEmpty()) {
            return new Attribute(UNIVERSAL_ID_ABBREV, new String[] { username });
        }

        if (email != null && !email.isEmpty()) {
            return new Attribute(MAIL, new String[] { email });
        }

        throw new BadRequestException("Username or email not provided in request");
    }

    private void anonymousUpdate(final ServerContext context, final ActionRequest request,
            final ResultHandler<JsonValue> handler) {
        String tokenID;
        String confirmationId;
        String username;
        String newPassword;
        final JsonValue jVal = request.getContent();

        try {
            tokenID = jVal.get(TOKEN_ID).asString();
            confirmationId = jVal.get(CONFIRMATION_ID).asString();
            username = jVal.get(USERNAME).asString();
            newPassword = jVal.get(USER_PASSWORD).asString();

            if (username == null || username.isEmpty()) {
                throw new BadRequestException("username not provided");
            }
            if (newPassword == null || newPassword.isEmpty()) {
                throw new BadRequestException("new password not provided");
            }

            validateToken(tokenID, realm, username, confirmationId);

            // update Identity
            SSOToken tok = RestUtils.getToken();
            Token admin = new Token();
            admin.setId(tok.getTokenID().toString());

            // Update instance with new password value
            if (updateInstance(admin, json(object(field(USERNAME, username), field(USER_PASSWORD, newPassword))),
                    handler)) {
                // Only remove the token if the update was successful, errors will be set in the handler.
                try {
                    // Even though the generated token will eventually timeout, delete it after a successful read
                    // so that the reset password request cannot be made again using the same token.
                    cts.delete(tokenID);
                } catch (DeleteFailedException e) {
                    // Catch this rather than letting it stop the process as it is possible that between successfully
                    // reading and deleting, the token has expired.
                    if (RestDispatcher.debug.messageEnabled()) {
                        RestDispatcher.debug.message("IdentityResource.anonymousUpdate(): Deleting token " + tokenID
                                + " after a successful read failed due to " + e.getMessage(), e);
                    }
                }
            }
        } catch (BadRequestException be) {
            RestDispatcher.debug.error("IdentityResource.anonymousUpdate():" + be.getMessage());
            handler.handleError(be);
        } catch (CoreTokenException cte) {
            RestDispatcher.debug.error("IdentityResource.anonymousUpdate():" + cte.getMessage());
            handler.handleError(new NotFoundException(cte.getMessage()));
        } catch (NotFoundException nfe) {
            RestDispatcher.debug.error("IdentityResource.anonymousUpdate():" + nfe.getMessage());
            handler.handleError(ResourceException.getException(HttpURLConnection.HTTP_GONE, nfe.getMessage(), nfe));
        } catch (Exception e) {
            RestDispatcher.debug.error("IdentityResource.anonymousUpdate():" + e.getMessage());
            handler.handleError(new NotFoundException(e.getMessage()));
        }

    }

    /**
     * Updates an instance given a JSON object with User Attributes
     * @param admin Token that has administrative privileges
     * @param details Json Value containing details of user identity
     * @param handler handles result of operation
     * @return true if the update was successful
     */
    private boolean updateInstance(Token admin, final JsonValue details, final ResultHandler<JsonValue> handler) {
        JsonValue jVal = details;
        IdentityDetails newDtls;
        IdentityServicesImpl idsvc;
        String resourceId = jVal.get(USERNAME).asString();

        boolean successfulUpdate = false;

        try {
            idsvc = new IdentityServicesImpl();
            newDtls = jsonValueToIdentityDetails(jVal);
            newDtls.setName(resourceId);

            // update resource with new details
            UpdateResponse message = idsvc.update(newDtls, admin);
            // read updated identity back to client
            IdentityDetails checkIdent = idsvc.read(resourceId, idSvcsAttrList, admin);
            // handle updated resource
            handler.handleResult(identityDetailsToJsonValue(checkIdent));
            successfulUpdate = true;
        } catch (final Exception exception) {
            RestDispatcher.debug.error("IdentityResource.updateInstance() :: Cannot UPDATE! " + exception);
            handler.handleError(new NotFoundException(exception.getMessage(), exception));
        }

        return successfulUpdate;
    }

    private void anonymousCreate(final ServerContext context, final ActionRequest request,
            final ResultHandler<JsonValue> handler, RestSecurity restSecurity) {

        final JsonValue jVal = request.getContent();
        String tokenID = null;
        String confirmationId;
        String email;

        try {
            if (!restSecurity.isSelfRegistration()) {
                throw new BadRequestException("Self-registration disabled");
            }

            tokenID = jVal.get(TOKEN_ID).asString();
            jVal.remove(TOKEN_ID);
            confirmationId = jVal.get(CONFIRMATION_ID).asString();
            jVal.remove(CONFIRMATION_ID);
            email = jVal.get(EMAIL).asString();

            if (email == null || email.isEmpty()) {
                throw new BadRequestException("Email not provided");
            }
            // Convert to IDRepo Attribute schema
            jVal.put("mail", email);

            if (confirmationId == null || confirmationId.isEmpty()) {
                throw new BadRequestException("confirmationId not provided");
            }

            validateToken(tokenID, realm, email, confirmationId);

            // create an Identity
            SSOToken tok = RestUtils.getToken();
            Token admin = new Token();
            admin.setId(tok.getTokenID().toString());
            if (createInstance(admin, jVal, handler)) {
                // Only remove the token if the create was successful, errors will be set in the handler.
                try {
                    // Even though the generated token will eventually timeout, delete it after a successful read
                    // so that the completed registration request cannot be made again using the same token.
                    cts.delete(tokenID);
                } catch (DeleteFailedException e) {
                    // Catch this rather than letting it stop the process as it is possible that between successfully
                    // reading and deleting, the token has expired.
                    if (RestDispatcher.debug.messageEnabled()) {
                        RestDispatcher.debug.message("IdentityResource.anonymousCreate: Deleting token " + tokenID
                                + " after a successful read failed due to " + e.getMessage(), e);
                    }
                }
            }
        } catch (BadRequestException be) {
            RestDispatcher.debug.error("IdentityResource.anonymousCreate() :: Invalid Parameter " + be);
            handler.handleError(be);
        } catch (NotFoundException nfe) {
            RestDispatcher.debug.error("IdentityResource.anonymousCreate(): Invalid tokenID : " + tokenID);
            handler.handleError(nfe);
        } catch (ServiceNotFoundException e) {
            // Failure from RestSecurity
            RestDispatcher.debug.error("Internal error", e);
            handler.handleError(
                    ResourceException.getException(ResourceException.INTERNAL_ERROR, e.getMessage(), e));
            return;
        } catch (CoreTokenException cte) { // For any unexpected CTS error
            RestDispatcher.debug.error("IdentityResource.anonymouseCreate(): CTS Error : " + cte.getMessage());
            handler.handleError(
                    ResourceException.getException(ResourceException.INTERNAL_ERROR, cte.getMessage(), cte));
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void actionInstance(final ServerContext context, final String resourceId, final ActionRequest request,
            final ResultHandler<JsonValue> handler) {
        final ResourceException e = new NotSupportedException("Actions are not supported for resource instances");
        handler.handleError(e);
    }

    /**
     * Creates an a resource using a privileged token
     * @param admin Token that has administrative privileges
     * @param details resource details that needs to be created
     * @param handler handles result of operation
     * @return true if the create was successful
     */
    private boolean createInstance(Token admin, final JsonValue details, final ResultHandler<JsonValue> handler) {

        JsonValue jVal = details;
        IdentityDetails dtls, identity;
        IdentityServicesImpl idsvc;
        String resourceId = null;

        boolean successfulCreate = false;

        try {
            idsvc = new IdentityServicesImpl();
            identity = jsonValueToIdentityDetails(jVal);
            resourceId = identity.getName();

            // Create the resource
            CreateResponse success = idsvc.create(identity, admin);
            // Read created resource
            dtls = idsvc.read(identity.getName(), idSvcsAttrList, admin);
            handler.handleResult(identityDetailsToJsonValue(dtls));
            successfulCreate = true;
        } catch (final ObjectNotFound notFound) {
            RestDispatcher.debug.error("IdentityResource.createInstance() :: Cannot READ " + resourceId
                    + ": Resource cannot be found." + notFound);
            handler.handleError(new NotFoundException("Resource not found.", notFound));
        } catch (final DuplicateObject duplicateObject) {
            RestDispatcher.debug.error("IdentityResource.createInstance() :: Cannot CREATE " + resourceId
                    + ": Resource already exists!" + duplicateObject);
            handler.handleError(new NotFoundException("Resource already exists", duplicateObject));
        } catch (final TokenExpired tokenExpired) {
            RestDispatcher.debug
                    .error("IdentityResource.createInstance() :: Cannot CREATE " + resourceId + ":" + tokenExpired);
            handler.handleError(new PermanentException(401, "Unauthorized", null));
        } catch (final NeedMoreCredentials needMoreCredentials) {
            RestDispatcher.debug.error("IdentityResource.createInstance() :: Cannot CREATE " + needMoreCredentials);
            handler.handleError(new ForbiddenException("Token is not authorized", needMoreCredentials));
        } catch (final Exception exception) {
            RestDispatcher.debug.error("IdentityResource.createInstance() :: Cannot CREATE! " + exception);
            handler.handleError(new NotFoundException(exception.getMessage(), exception));
        }

        return successfulCreate;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void createInstance(final ServerContext context, final CreateRequest request,
            final ResultHandler<Resource> handler) {
        // anyone can create an account add
        Token admin = new Token();
        admin.setId(getCookieFromServerContext(context));

        final JsonValue jVal = request.getContent();
        IdentityDetails dtls, identity;
        Resource resource;
        IdentityServicesImpl idsvc;
        String resourceId = request.getNewResourceId();

        try {
            idsvc = new IdentityServicesImpl();
            identity = jsonValueToIdentityDetails(jVal);
            // check to see if request has included resource ID
            if (resourceId != null) {
                if (identity.getName() != null) {
                    if (!resourceId.equalsIgnoreCase(identity.getName())) {
                        throw new BadRequestException("id in path does not match id in request body");
                    }
                }
                identity.setName(resourceId);
            } else {
                resourceId = identity.getName();
            }

            // Create the resource
            CreateResponse success = idsvc.create(identity, admin);
            // Read created resource
            dtls = idsvc.read(resourceId, idSvcsAttrList, admin);

            resource = new Resource(resourceId, "0", identityDetailsToJsonValue(dtls));
            handler.handleResult(resource);
        } catch (final ObjectNotFound notFound) {
            RestDispatcher.debug.error("IdentityResource.createInstance() :: Cannot READ " + resourceId
                    + ": Resource cannot be found." + notFound);
            handler.handleError(new NotFoundException("Resource not found.", notFound));
        } catch (final DuplicateObject duplicateObject) {
            RestDispatcher.debug.error("IdentityResource.createInstance() :: Cannot CREATE " + resourceId
                    + ": Resource already exists!" + duplicateObject);
            handler.handleError(new NotFoundException("Resource already exists", duplicateObject));
        } catch (final TokenExpired tokenExpired) {
            RestDispatcher.debug
                    .error("IdentityResource.createInstance() :: Cannot CREATE " + resourceId + ":" + tokenExpired);
            handler.handleError(new PermanentException(401, "Unauthorized", null));
        } catch (final NeedMoreCredentials needMoreCredentials) {
            RestDispatcher.debug.error("IdentityResource.createInstance() :: Cannot CREATE " + needMoreCredentials);
            handler.handleError(new ForbiddenException("Token is not authorized", needMoreCredentials));
        } catch (BadRequestException be) {
            RestDispatcher.debug.error("IdentityResource.createInstance() :: Cannot CREATE " + be);
            handler.handleError(be);
        } catch (final Exception exception) {
            RestDispatcher.debug.error("IdentityResource.createInstance() :: Cannot CREATE! " + exception);
            handler.handleError(new NotFoundException(exception.getMessage(), exception));
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteInstance(final ServerContext context, final String resourceId, final DeleteRequest request,
            final ResultHandler<Resource> handler) {
        Token admin = new Token();
        admin.setId(getCookieFromServerContext(context));

        JsonValue result = new JsonValue(new LinkedHashMap<String, Object>(1));
        Resource resource;
        IdentityDetails dtls;
        IdentityServicesImpl idsvc = new IdentityServicesImpl();

        try {

            // read to see if resource is available to user
            dtls = idsvc.read(resourceId, idSvcsAttrList, admin);

            // delete the resource
            DeleteResponse success = idsvc.delete(dtls, admin);

            result.put("success", "true");
            resource = new Resource(resourceId, "0", result);
            handler.handleResult(resource);

        } catch (final NeedMoreCredentials ex) {
            RestDispatcher.debug.error("IdentityResource.deleteInstance() :: Cannot DELETE " + resourceId
                    + ": User does not have enough privileges.");
            handler.handleError(new ForbiddenException(resourceId, ex));
        } catch (final ObjectNotFound notFound) {
            RestDispatcher.debug
                    .error("IdentityResource.deleteInstance() :: Cannot DELETE " + resourceId + ":" + notFound);
            handler.handleError(new NotFoundException("Resource cannot be found.", notFound));
        } catch (final TokenExpired tokenExpired) {
            RestDispatcher.debug
                    .error("IdentityResource.deleteInstance() :: Cannot DELETE " + resourceId + ":" + tokenExpired);
            handler.handleError(new PermanentException(401, "Unauthorized", null));
        } catch (final AccessDenied accessDenied) {
            RestDispatcher.debug
                    .error("IdentityResource.deleteInstance() :: Cannot DELETE " + resourceId + ":" + accessDenied);
            handler.handleError(new ForbiddenException(accessDenied.getMessage(), accessDenied));
        } catch (final GeneralFailure generalFailure) {
            RestDispatcher.debug
                    .error("IdentityResource.deleteInstance() :: Cannot DELETE " + generalFailure.getMessage());
            handler.handleError(new BadRequestException(generalFailure.getMessage(), generalFailure));
        } catch (final Exception exception) {
            RestDispatcher.debug
                    .error("IdentityResource.deleteInstance() :: Cannot DELETE! " + exception.getMessage());
            result.put("success", "false");
            resource = new Resource(resourceId, "0", result);
            handler.handleResult(resource);
        }
    }

    /**
     * Returns a JsonValue containing appropriate identity details
     *
     * @param details The IdentityDetails of a Resource
     * @return The JsonValue Object
     */
    private JsonValue identityDetailsToJsonValue(IdentityDetails details) {
        JsonValue result = new JsonValue(new LinkedHashMap<String, Object>(1));
        try {
            result.put(USERNAME, details.getName());
            result.put("realm", details.getRealm());
            Attribute[] attrs = details.getAttributes();

            for (Attribute aix : attrs) {
                result.put(aix.getName(), aix.getValues());
            }
            return result;
        } catch (final Exception e) {
            throw new JsonValueException(result);
        }
    }

    /**
     * Returns an IdentityDetails from a JsonValue
     *
     * @param jVal The JsonValue Object to be converted
     * @return The IdentityDetails object
     */
    private IdentityDetails jsonValueToIdentityDetails(JsonValue jVal) {

        IdentityDetails identity = new IdentityDetails();
        List<Attribute> identityAttrList = new ArrayList();

        try {
            identity.setType(userType); //set type ex. user
            identity.setRealm(realm); //set realm
            identity.setName(jVal.get(USERNAME).asString());//set name from JsonValue object

            try {
                for (String s : jVal.keys()) {
                    JsonValue childValue = jVal.get(s);
                    if (childValue.isString()) {
                        String[] tArray = { childValue.asString() };
                        identityAttrList.add(new Attribute(s, tArray));
                    } else if (childValue.isList()) {
                        ArrayList<String> tList = (ArrayList<String>) childValue.getObject();
                        String[] tArray = tList.toArray(new String[tList.size()]);
                        identityAttrList.add(new Attribute(s, tArray));
                    }
                }
            } catch (Exception e) {
                RestDispatcher.debug.error(
                        "IdentityResource.jsonValueToIdentityDetails() :: " + "Cannot Traverse JsonValue" + e);
            }
            Attribute[] attr = identityAttrList.toArray(new Attribute[identityAttrList.size()]);
            identity.setAttributes(attr);

        } catch (final Exception e) {
            RestDispatcher.debug.error("IdentityResource.jsonValueToIdentityDetails() ::"
                    + " Cannot convert JsonValue to IdentityDetails." + e);
            //deal with better exceptions
        }
        return identity;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void patchInstance(final ServerContext context, final String resourceId, final PatchRequest request,
            final ResultHandler<Resource> handler) {
        final ResourceException e = new NotSupportedException("Patch operations are not supported");
        handler.handleError(e);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void queryCollection(final ServerContext context, final QueryRequest request,
            final QueryResultHandler handler) {

        Token admin = new Token();
        admin.setId(getCookieFromServerContext(context));

        String queryFilter;

        try {
            // This will only return 1 user..
            // getQueryFilter() is not implemented yet..returns dummy false value
            queryFilter = request.getQueryId();
            if (queryFilter == null || queryFilter.isEmpty()) {
                queryFilter = "*";
            }
            IdentityServicesImpl id = new IdentityServicesImpl();
            List<String> users = id.search(queryFilter, idSvcsAttrList, admin);

            for (final String user : users) {
                JsonValue val = new JsonValue(user);
                Resource resource = new Resource(user, "0", val);
                handler.handleResource(resource);
            }
        } catch (Exception ex) {

        }

        handler.handleResult(new QueryResult());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readInstance(final ServerContext context, final String resourceId, final ReadRequest request,
            final ResultHandler<Resource> handler) {

        Token admin = new Token();
        admin.setId(getCookieFromServerContext(context));

        IdentityServicesImpl idsvc;
        IdentityDetails dtls;
        Resource resource;

        try {
            idsvc = new IdentityServicesImpl();
            dtls = idsvc.read(resourceId, idSvcsAttrList, admin);
            resource = new Resource(resourceId, "0", identityDetailsToJsonValue(dtls));
            handler.handleResult(resource);
        } catch (final NeedMoreCredentials needMoreCredentials) {
            RestDispatcher.debug.error(
                    "IdentityResource.readInstance() :: Cannot READ " + resourceId + ":" + needMoreCredentials);
            handler.handleError(
                    new ForbiddenException("User does not have enough privileges.", needMoreCredentials));
        } catch (final ObjectNotFound objectNotFound) {
            RestDispatcher.debug
                    .error("IdentityResource.readInstance() :: Cannot READ " + resourceId + ":" + objectNotFound);
            handler.handleError(new NotFoundException("Resource cannot be found.", objectNotFound));
        } catch (final TokenExpired tokenExpired) {
            RestDispatcher.debug
                    .error("IdentityResource.readInstance() :: Cannot READ " + resourceId + ":" + tokenExpired);
            handler.handleError(new PermanentException(401, "Unauthorized", null));
        } catch (final AccessDenied accessDenied) {
            RestDispatcher.debug
                    .error("IdentityResource.readInstance() :: Cannot READ " + resourceId + ":" + accessDenied);
            handler.handleError(new ForbiddenException(accessDenied.getMessage(), accessDenied));
        } catch (final GeneralFailure generalFailure) {
            RestDispatcher.debug.error("IdentityResource.readInstance() :: Cannot READ " + generalFailure);
            handler.handleError(new BadRequestException(generalFailure.getMessage(), generalFailure));
        } catch (final Exception exception) {
            RestDispatcher.debug.error("IdentityResource.readInstance() :: Cannot READ! " + exception);
            handler.handleError(new NotFoundException(exception.getMessage(), exception));

        }
    }

    private String getPasswordFromHeader(ServerContext context) {
        List<String> headerList = null;
        String oldUserPasswordHeaderName = "olduserpassword";
        HttpContext header = null;

        try {
            header = context.asContext(HttpContext.class);
            if (header == null) {
                RestDispatcher.debug.error("IdentityResource.getPasswordFromHeader :: "
                        + "Cannot retrieve ServerContext as HttpContext");
                return null;
            }
            //get the oldusername from header directly
            headerList = header.getHeaders().get(oldUserPasswordHeaderName.toLowerCase());
            if (headerList != null && !headerList.isEmpty()) {
                for (String s : headerList) {
                    return (s != null && !s.isEmpty()) ? s : null;
                }
            }
        } catch (Exception e) {
            RestDispatcher.debug.error("IdentityResource.getPasswordFromHeader :: "
                    + "Cannot get olduserpassword from ServerContext!" + e);
        }
        return null;
    }

    @Override
    public void updateInstance(final ServerContext context, final String resourceId, final UpdateRequest request,
            final ResultHandler<Resource> handler) {

        Token admin = new Token();
        admin.setId(getCookieFromServerContext(context));

        final JsonValue jsonValue = request.getNewContent();
        IdentityDetails dtls, newDtls;
        IdentityServicesImpl idsvc = new IdentityServicesImpl();
        Resource resource;
        try {
            // Retrieve details about user to be updated
            dtls = idsvc.read(resourceId, idSvcsAttrList, admin);

            //check first if the password is modified as part of the update request, so if necessary, the password can
            //be removed from the IdentityDetails object.
            if (!isAdmin(context)) {
                for (String attrName : jsonValue.keys()) {
                    if ("userpassword".equalsIgnoreCase(attrName)) {
                        String newPassword = jsonValue.get(attrName).asString();
                        if (!isNullOrEmpty(newPassword)) {
                            String oldPassword = getPasswordFromHeader(context);
                            if (isNullOrEmpty(oldPassword)) {
                                throw new BadRequestException("The old password is missing from the request");
                            }
                            //This is an end-user trying to change the password, so let's change the password by
                            //verifying that the provided old password is correct. We also remove the password from the
                            //list of attributes to prevent the administrative password reset via the update call.
                            jsonValue.remove(attrName);
                            changePassword(context, handler, resourceId, oldPassword, newPassword);
                        }
                        break;
                    }
                }
            }
            newDtls = jsonValueToIdentityDetails(jsonValue);
            if (newDtls.getName() != null && !resourceId.equalsIgnoreCase(newDtls.getName())) {
                throw new BadRequestException("id in path does not match id in request body");
            }
            newDtls.setName(resourceId);

            // update resource with new details
            UpdateResponse message = idsvc.update(newDtls, admin);
            // read updated identity back to client
            IdentityDetails checkIdent = idsvc.read(dtls.getName(), idSvcsAttrList, admin);
            // handle updated resource
            resource = new Resource(resourceId, "0", identityDetailsToJsonValue(checkIdent));
            handler.handleResult(resource);
        } catch (final ObjectNotFound onf) {
            RestDispatcher.debug.error("IdentityResource.updateInstance() :: Cannot UPDATE! " + onf);
            handler.handleError(
                    new NotFoundException("Could not find the resource [ " + resourceId + " ] to update", onf));
        } catch (final NeedMoreCredentials needMoreCredentials) {
            RestDispatcher.debug.error(
                    "IdentityResource.updateInstance() :: Cannot UPDATE " + resourceId + ":" + needMoreCredentials);
            handler.handleError(new ForbiddenException("Token is not authorized", needMoreCredentials));
        } catch (final TokenExpired tokenExpired) {
            RestDispatcher.debug
                    .error("IdentityResource.updateInstance() :: Cannot UPDATE " + resourceId + ":" + tokenExpired);
            handler.handleError(new PermanentException(401, "Unauthorized", null));
        } catch (final AccessDenied accessDenied) {
            RestDispatcher.debug
                    .error("IdentityResource.updateInstance() :: Cannot UPDATE " + resourceId + ":" + accessDenied);
            handler.handleError(new ForbiddenException(accessDenied.getMessage(), accessDenied));
        } catch (final GeneralFailure generalFailure) {
            RestDispatcher.debug.error("IdentityResource.updateInstance() :: Cannot UPDATE " + generalFailure);
            handler.handleError(new BadRequestException(generalFailure.getMessage(), generalFailure));
        } catch (final ResourceException re) {
            RestDispatcher.debug
                    .error("IdentityResource.updateInstance() :: Cannot UPDATE! " + resourceId + ":" + re);
            handler.handleError(re);
        } catch (final Exception exception) {
            RestDispatcher.debug.error("IdentityResource.updateInstance() :: Cannot UPDATE! " + exception);
            handler.handleError(new NotFoundException(exception.getMessage(), exception));
        }
    }

    private List<Attribute> getIdentityServicesAttributes(String realm) {

        final List<Attribute> identityServicesAttributes = new ArrayList<Attribute>();

        String[] userTypeVal = { userType };
        String[] realmVal = { realm };
        identityServicesAttributes.add(new Attribute("objecttype", userTypeVal));
        identityServicesAttributes.add(new Attribute("realm", realmVal));

        return identityServicesAttributes;
    }

    private void changePassword(ServerContext serverContext, ResultHandler<Resource> handler, String username,
            String oldPassword, String newPassword) throws ResourceException {
        try {
            SSOTokenManager tokenManager = SSOTokenManager.getInstance();
            SSOToken requesterToken = tokenManager.createSSOToken(getCookieFromServerContext(serverContext));
            if (tokenManager.isValidToken(requesterToken)) {
                AMIdentity userIdentity = new AMIdentity(requesterToken, username, IdType.USER, realm, null);
                userIdentity.changePassword(oldPassword, newPassword);
            }
        } catch (SSOException ssoe) {
            RestDispatcher.debug
                    .warning("IdentityResource.changePassword() :: SSOException occurred while changing "
                            + "the password for user: " + username, ssoe);
            throw new PermanentException(401, "An error occurred while trying to change the password", ssoe);
        } catch (IdRepoException ire) {
            if ("402".equals(ire.getErrorCode())) {
                throw new ForbiddenException("The user is not authorized to change the password");
            } else {
                RestDispatcher.debug.warning("IdentityResource.changePassword() :: IdRepoException occurred while "
                        + "changing the password for user: " + username, ire);
                throw new InternalServerErrorException("An error occurred while trying to change the password",
                        ire);
            }
        }
    }

    private String requestParamEncode(String toEncode) throws UnsupportedEncodingException {
        if (toEncode != null && !toEncode.isEmpty()) {
            return URLEncoder.encode(toEncode, "UTF-8").replace("+", "%20");
        } else {
            return toEncode;
        }
    }

    /**
     * Retrieve cached realm's RestSecurity instance
     **/
    private RestSecurity getRestSecurity(String realm) {
        RestSecurity restSecurity = REALM_REST_SECURITY_MAP.get(realm);
        if (restSecurity == null) {
            synchronized (REALM_REST_SECURITY_MAP) {
                restSecurity = REALM_REST_SECURITY_MAP.get(realm);
                if (restSecurity == null) {
                    restSecurity = new RestSecurity(realm);
                    REALM_REST_SECURITY_MAP.put(realm, restSecurity);
                }
            }
        }
        return restSecurity;
    }

    private static boolean isNullOrEmpty(final String value) {
        return value == null || value.isEmpty();
    }
}