IdentityResourceV2.java revision 2dd75eff92ef66e22cca286b6f4fe5a9c929af9d
/*
* 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.
*/
/**
* A simple {@code Map} based collection resource provider.
*/
public final class IdentityResourceV2 implements CollectionResourceProvider {
// TODO: filters, sorting, paged results.
private final RestSecurityProvider restSecurityProvider;
private ServiceConfigManager mailmgr;
private ServiceConfig mailscm;
private final MailServerLoader mailServerLoader;
private final IdentityResourceV1 identityResourceV1;
private final IdentityServicesImpl identityServices;
/**
* Creates a backend
*/
public IdentityResourceV2(String userType, MailServerLoader mailServerLoader, IdentityServicesImpl identityServices,
}
/**
* Enum to lazy init the CTSPersistentStore variable in a thread safe manner.
*/
private enum CTSHolder {
private final CTSPersistentStore cts;
private CTSHolder() {
}
static CTSPersistentStore getCTS() {
}
}
// Constructor used for testing...
this.mailServerLoader = mailServerLoader;
this.identityResourceV1 = new IdentityResourceV1(userType, mailServerLoader, identityServices, coreWrapper,
this.identityServices = identityServices;
}
/**
* Gets the user id from the session provided in the server context
*
* @param context Current Server Context
* @param handler Result handler
*/
try {
// build resource
if (debug.messageEnabled()) {
debug.message("IdentityResource.idFromSession() :: Retrieved ID for user, " + amIdentity.getName());
}
} catch (SSOException e) {
} catch (IdRepoException ex) {
}
}
/*
Check the scenario when a user with an active session is redirected back to the XUI without the relative realm
in the URI path. The XUI will fail to get the users profile unless we can determine what is the relative realm
based from the users realm and the base realm from the realm context.
*/
}
return realm;
}
}
return relativeRealm;
}
/**
* Generates a secure hash to use as token ID
* @param resource string that will be used to create random hash
* @return random string
*/
return null;
}
}
/**
* 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
*/
}
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 String realm,
try {
if (restSecurity == null) {
if (debug.warningEnabled()) {
"Rest Security not created. restSecurity = " + restSecurity);
}
throw new NotFoundException("Rest Security Service not created" );
}
if (!restSecurity.isSelfRegistration()) {
if (debug.warningEnabled()) {
}
throw new NotSupportedException("Self Registration is not enabled.");
}
// Get full deployment URL
// Get the email address provided from registration page
throw new BadRequestException("Email not provided");
}
// Retrieve email registration token life time
// Create CTS Token
// Store token in datastore
// Create confirmationId
String confirmationId = Hash.hash(tokenID + emailAddress + SystemProperties.get(AM_ENCRYPTION_PWD));
// Build Confirmation URL
} else {
}
confirmationLink = confURLBuilder.append("?confirmationId=").append(requestParamEncode(confirmationId))
.toString();
// Send Registration
if (debug.messageEnabled()) {
debug.message("IdentityResource.createRegistrationEmail() :: Sent notification to, " + emailAddress +
}
} catch (BadRequestException be) {
if (debug.warningEnabled()) {
+ be.getMessage());
}
} catch (NotFoundException nfe) {
if (debug.warningEnabled()) {
debug.warning("IdentityResource.createRegistrationEmail(): Cannot send email to : " + emailAddress, nfe);
}
} catch (NotSupportedException nse) {
if (debug.warningEnabled()) {
}
} catch (Exception e) {
if (debug.errorEnabled()) {
debug.error("IdentityResource.createRegistrationEmail(): Cannot send email to : " + emailAddress, e);
}
}
}
/**
* 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
*/
try {
} catch (SMSException smse) {
if (debug.errorEnabled()) {
}
throw new InternalServerErrorException("Cannot create the service: " + MailServerImpl.SERVICE_NAME, smse);
} catch (SSOException ssoe) {
if (debug.errorEnabled()) {
}
throw new InternalServerErrorException("Cannot create the service: " + MailServerImpl.SERVICE_NAME, ssoe);
}
if (debug.errorEnabled()) {
}
}
// Get MailServer Implementation class
try {
} catch (IllegalStateException e) {
throw new InternalServerErrorException(error, e);
}
try {
// Check if subject has not been included
// Use default email service subject
}
} catch (Exception e) {
if (debug.warningEnabled()) {
}
subject = "";
}
try {
// Check if Custom Message has been included
// Use default email service message
}
} catch (Exception e) {
if (debug.warningEnabled()) {
}
}
// Send the emails via the implementation class
try {
} catch (MessagingException e) {
if (debug.errorEnabled()) {
}
throw new InternalServerErrorException(error, e);
}
}
/**
* Validates the current goto against the list of allowed gotos, and returns either the allowed
* goto as sent in, or the server's default goto value.
*
* @param context Current Server Context
* @param request Request from client to confirm registration
* @param handler Result handler
*/
try {
} catch (SSOException ssoe){
if (debug.errorEnabled()) {
}
handler.handleError(ResourceException.getException(ResourceException.FORBIDDEN, ssoe.getMessage(), ssoe));
}
}
/**
* Will validate confirmationId is correct
* @param request Request from client to confirm registration
* @param handler Result handler
*/
//email or username value used to create confirmationId
try{
if (debug.errorEnabled()) {
}
throw new BadRequestException("confirmationId not provided");
}
}
}
if (debug.errorEnabled()) {
}
throw new BadRequestException("Required information not provided");
}
if (debug.errorEnabled()) {
}
throw new BadRequestException("tokenId not provided");
}
// build resource
if (debug.messageEnabled()) {
debug.message(METHOD + ": Confirmed for token, " + tokenID + ", with confirmation " + confirmationId);
}
} catch (BadRequestException be){
} catch (Exception e){
}
}
/**
* 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 IdentityResourceV2#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, not null
* @param hashComponent The hash component used to created the confirmationId
* @param confirmationId The confirmationId, not null
* @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)
//check expiry
if (ctsToken == null || TimeUtils.toUnixTime(ctsToken.getExpiryTimestamp()) < TimeUtils.currentUnixTime()) {
}
// check confirmationId
}
//check realm
}
if (debug.messageEnabled()) {
+ confirmationId);
}
}
/**
* {@inheritDoc}
*/
} else { // for now this is the only case coming in, so fail if otherwise
}
}
/**
* Uses an amAdmin SSOtoken to create an AMIdentity from the UID provided and checks
* @param uid the universal identifier of the user
* @return true is the user is active;false otherwise
* @throws NotFoundException invalid SSOToken, invalid UID
*/
try {
if (debug.messageEnabled()) {
userIdentity.isActive());
}
return userIdentity.isActive();
} catch (IdRepoException idr) {
if (debug.errorEnabled()) {
}
} catch (SSOException ssoe){
if (debug.errorEnabled()) {
}
}
}
/**
* Generates the e-mail contents based on the incoming request.
*
* Will only send the e-mail if all the following conditions are true:
*
* - Forgotten Password service is enabled
* - User exists
* - User has an e-mail address in their profile
* - E-mail service is correctly configured.
*
* @param context Non null.
* @param request Non null.
* @param realm Used as part of user lookup.
* @param restSecurity Non null.
* @param handler Required for return response to caller.
*/
private void generateNewPasswordEmail(final ServerContext context, final ActionRequest request, final String realm,
try {
// Check to make sure forgotPassword enabled
if (restSecurity == null) {
if (debug.warningEnabled()) {
}
throw ResourceException.getException(ResourceException.UNAVAILABLE, "Rest Security Service not created");
}
if (!restSecurity.isForgotPassword()) {
if (debug.warningEnabled()) {
}
throw ResourceException.getException(ResourceException.UNAVAILABLE, "Forgot password is not accessible.");
}
// Generate Admin Token
if (searchResults.isEmpty()) {
throw new NotFoundException("User not found");
throw new ConflictException("Multiple users found");
} else {
for (Map.Entry<String, Set<String>> attribute : asMap(identityDetails.getAttributes()).entrySet()) {
}
}
}
}
if (!isUserActive(uid)) {
throw new ForbiddenException("Request is forbidden for this user");
}
// Check if email is provided
throw new BadRequestException("No email provided in profile.");
}
// Get full deployment URL
// Retrieve email registration token life time
if (restSecurity == null) {
if (debug.warningEnabled()) {
}
throw new NotFoundException("Rest Security Service not created");
}
// Generate Token
// Store token in datastore
// Create confirmationId
// Build Confirmation URL
} else {
}
String confirmationLink = confURLBuilder.append("?confirmationId=").append(requestParamEncode(confirmationId))
.toString();
// Send Registration
if (debug.messageEnabled()) {
debug.message("IdentityResource.generateNewPasswordEmail :: ACTION of generate new password email " +
}
}
} catch (ResourceException re) {
} catch (Exception e) {
// Intentional - all other errors are considered Internal Error.
handler.handleError(ResourceException.getException(ResourceException.INTERNAL_ERROR, "Failed to send mail", e));
}
}
private Map<String, Set<String>> getAttributeFromRequest(JsonValue jsonBody) throws BadRequestException {
throw new BadRequestException("Both username and email specified - only one allowed in request.");
}
}
}
throw new BadRequestException("Username or email not provided in request");
}
/**
* Perform an anonymous update of a user's password using the provided token.
*
* The token must match a token placed in the CTS in order for the request
* to proceed.
*
* @param context Non null
* @param request Non null
* @param realm Non null
* @param handler Non null
*/
private void anonymousUpdate(final ServerContext context, final ActionRequest request, final String realm,
try{
throw new BadRequestException("username not provided");
}
throw new BadRequestException("new password not provided");
}
// update Identity
// Update instance with new password value
// 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.
} 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 (debug.messageEnabled()) {
"read failed due to " + e.getMessage(), e);
}
}
}
handler.handleError(ResourceException.getException(ResourceException.INTERNAL_ERROR, cte.getMessage(), cte));
} catch (NotFoundException nfe) {
handler.handleError(ResourceException.getException(HttpURLConnection.HTTP_GONE, nfe.getMessage(), nfe));
}
}
/**
* 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
*/
boolean successfulUpdate = false;
try {
throw new BadRequestException("Illegal arguments: One or more required arguments is null or empty");
}
// update resource with new details
// read updated identity back to client
IdentityDetails checkIdent = identityServices.read(resourceId, getIdentityServicesAttributes(realm),
admin);
// handle updated resource
successfulUpdate = true;
}
return successfulUpdate;
}
private void anonymousCreate(final ServerContext context, final ActionRequest request, final String realm,
try{
if (!restSecurity.isSelfRegistration()) {
throw new BadRequestException("Self-registration disabled");
}
throw new BadRequestException("Email not provided");
}
// Convert to IDRepo Attribute schema
throw new BadRequestException("confirmationId not provided");
}
throw new BadRequestException("tokenId not provided");
}
// create an Identity
// 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.
} 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 (debug.messageEnabled()) {
" after a successful read failed due to " + e.getMessage(), e);
}
}
}
} catch (BadRequestException be){
} catch (NotFoundException nfe){
handler.handleError(ResourceException.getException(ResourceException.INTERNAL_ERROR, cte.getMessage(), cte));
} catch (ServiceNotFoundException e) {
// Failure from RestSecurity
handler.handleError(ResourceException.getException(ResourceException.INTERNAL_ERROR, e.getMessage(), e));
return;
}
}
/**
* {@inheritDoc}
*/
public void actionInstance(final ServerContext context, final String resourceId, final ActionRequest request,
try {
}
}
if (debug.messageEnabled()) {
}
} catch (ResourceException re) {
}
} else {
}
}
/**
* 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
*
*/
boolean successfulCreate = false;
successfulCreate = true;
}
return successfulCreate;
}
/**
* {@inheritDoc}
*/
try {
// anyone can create an account add
// check to see if request has included resource ID
if (resourceId != null) {
return;
}
}
} else {
}
" performed by " + principalName);
}
} catch (SSOException e) {
}
}
private IdentityDetails attemptResourceCreation(ResultHandler<?> handler, String realm, SSOToken admin,
try {
// Create the resource
// Read created resource
if (debug.messageEnabled()) {
}
} catch (final ObjectNotFound notFound) {
} catch (final TokenExpired tokenExpired) {
debug.error("IdentityResource.createInstance() :: Cannot CREATE " + resourceId + ":" + tokenExpired);
} catch (final NeedMoreCredentials needMoreCredentials) {
} catch (final GeneralAccessDeniedError accessDenied) {
} catch (GeneralFailure generalFailure) {
} catch (AccessDenied accessDenied) {
} catch (ResourceException e) {
handler.handleError(e);
}
return dtls;
}
/**
* {@inheritDoc}
*/
public void deleteInstance(final ServerContext context, final String resourceId, final DeleteRequest request,
}
/**
* Returns a JsonValue containing appropriate identity details
*
* @param details The IdentityDetails of a Resource
* @return The JsonValue Object
*/
try {
}
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
*/
try {
try {
if (childValue.isString()) {
} else if (childValue.isList()) {
}
}
} catch (Exception e) {
"Cannot Traverse JsonValue" + e);
}
} catch (final Exception e) {
" Cannot convert JsonValue to IdentityDetails." + e);
//deal with better exceptions
}
return identity;
}
/**
* {@inheritDoc}
*/
public void patchInstance(final ServerContext context, final String resourceId, final PatchRequest request,
}
/**
* {@inheritDoc}
*/
final QueryResultHandler handler) {
}
/**
* {@inheritDoc}
*/
public void readInstance(final ServerContext context, final String resourceId, final ReadRequest request,
}
private boolean checkValidPassword(String username, char[] password, String realm) throws BadRequestException {
throw new BadRequestException("Invalid Username or Password");
}
try {
if (debug.messageEnabled()) {
}
return false;
}
}
/**
* {@inheritDoc}
*/
public void updateInstance(final ServerContext context, final String resourceId, final UpdateRequest request,
try {
// Retrieve details about user to be updated
// Continue modifying the identity if read success
+ "Use POST with _action=changePassword or _action=forgotPassword."));
return;
}
}
throw new BadRequestException("Illegal arguments: One or more required arguments is null or empty");
}
throw new BadRequestException("id in path does not match id in request body");
}
// Handle attribute change when password is required
// Get restSecurity for this realm
// Make sure user is not admin and check to see if we are requiring a password to change any attributes
boolean hasReauthenticated = false;
// If attribute is not available set newAttr variable to empty string for use in comparison
// Get the value of current attribute
}
}
// Compare newAttr and currentAttr
// check header to make sure that password is there then check to see if it's correct
//set a boolean value so we know reauth has been done
hasReauthenticated = true;
//continue will allow attribute(s) change(s)
} else {
throw new BadRequestException("Must provide a valid confirmation password to change " +
newAttr + "'");
}
}
}
}
}
// update resource with new details
" performed by " + principalName);
// read updated identity back to client
// handle updated resource
} catch (final ObjectNotFound onf) {
onf);
handler.handleError(new NotFoundException("Could not find the resource [ " + resourceId + " ] to update", onf));
} catch (final NeedMoreCredentials needMoreCredentials) {
} catch (final TokenExpired tokenExpired) {
} catch (final AccessDenied accessDenied) {
} catch (final GeneralFailure generalFailure) {
} catch (BadRequestException bre){
} catch (NotFoundException e) {
handler.handleError(new NotFoundException("Could not find the resource [ " + resourceId + " ] to update",
e));
} catch (ResourceException e) {
handler.handleError(e);
}
}
return identityServicesAttributes;
}
} else {
return toEncode;
}
}
}