Merged WEBAPP-API (5.2.1) to 5.2.N (5.2.1)

135590 jkaabimofrad: APPSREPO-35: Added password reset V1 API.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@135930 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jamal Kaabi-Mofrad
2017-03-16 19:39:24 +00:00
parent e582e1a626
commit cded2f354d
6 changed files with 297 additions and 15 deletions

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,6 +25,7 @@
*/
package org.alfresco.rest.api;
import org.alfresco.rest.api.model.PasswordReset;
import org.alfresco.rest.api.model.Person;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
@@ -35,15 +36,15 @@ import java.util.List;
public interface People
{
String DEFAULT_USER = "-me-";
String DEFAULT_USER = "-me-";
String PARAM_INCLUDE_ASPECTNAMES = "aspectNames";
String PARAM_INCLUDE_PROPERTIES = "properties";
String PARAM_FIRST_NAME = "firstName";
String PARAM_LAST_NAME = "lastName";
String PARAM_ID = "id";
String validatePerson(String personId);
String validatePerson(String personId, boolean validateIsCurrentUser);
String validatePerson(String personId);
String validatePerson(String personId, boolean validateIsCurrentUser);
NodeRef getAvatar(String personId);
/**
@@ -85,4 +86,20 @@ public interface People
* @return CollectionWithPagingInfo<Person>
*/
CollectionWithPagingInfo<Person> getPeople(Parameters parameters);
/**
* Request password reset (an email will be sent to the registered email of the given {@code userId}).
* The API returns a 202 response for a valid, as well as the invalid (does not exist or disabled) userId
*
* @param userId the user id of the person requesting the password reset
* @param client the client name which is registered to send emails
*/
void requestPasswordReset(String userId, String client);
/**
* Performs password reset
*
* @param passwordReset the password reset details
*/
void resetPassword(String personId, PasswordReset passwordReset);
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -31,13 +31,20 @@ import org.alfresco.query.PagingResults;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.authentication.ResetPasswordService;
import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.InvalidResetPasswordWorkflowException;
import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.ResetPasswordDetails;
import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.ResetPasswordWorkflowInvalidUserException;
import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.ResetPasswordWorkflowNotFoundException;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.People;
import org.alfresco.rest.api.Sites;
import org.alfresco.rest.api.model.PasswordReset;
import org.alfresco.rest.api.model.Person;
import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.NotFoundException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
@@ -57,6 +64,8 @@ import org.alfresco.service.cmr.usage.ContentUsageService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.Serializable;
import java.util.AbstractList;
@@ -76,6 +85,8 @@ import java.util.Set;
*/
public class PeopleImpl implements People
{
private static final Log LOGGER = LogFactory.getLog(PeopleImpl.class);
private static final List<String> EXCLUDED_NS = Arrays.asList(
NamespaceService.SYSTEM_MODEL_1_0_URI,
"http://www.alfresco.org/model/user/1.0",
@@ -99,6 +110,7 @@ public class PeopleImpl implements People
protected ContentUsageService contentUsageService;
protected ContentService contentService;
protected ThumbnailService thumbnailService;
protected ResetPasswordService resetPasswordService;
private final static Map<String, QName> sort_params_to_qnames;
static
@@ -160,17 +172,24 @@ public class PeopleImpl implements People
this.thumbnailService = thumbnailService;
}
public void setResetPasswordService(ResetPasswordService resetPasswordService)
{
this.resetPasswordService = resetPasswordService;
}
/**
* Validate, perform -me- substitution and canonicalize the person ID.
*
* @param personId
* @return The validated and processed ID.
*/
@Override
public String validatePerson(String personId)
{
return validatePerson(personId, false);
}
@Override
public String validatePerson(final String requestedPersonId, boolean validateIsCurrentUser)
{
String personId = requestedPersonId;
@@ -237,7 +256,7 @@ public class PeopleImpl implements People
});
}
}
public boolean hasAvatar(NodeRef personNodeRef)
{
if(personNodeRef != null)
@@ -251,6 +270,7 @@ public class PeopleImpl implements People
}
}
@Override
public NodeRef getAvatar(String personId)
{
NodeRef avatar = null;
@@ -292,6 +312,7 @@ public class PeopleImpl implements People
* @throws NoSuchPersonException
* if personId does not exist
*/
@Override
public Person getPerson(String personId)
{
personId = validatePerson(personId);
@@ -311,6 +332,7 @@ public class PeopleImpl implements People
return person;
}
@Override
public CollectionWithPagingInfo<Person> getPeople(final Parameters parameters)
{
Paging paging = parameters.getPaging();
@@ -585,6 +607,7 @@ public class PeopleImpl implements People
}
}
@Override
public Person update(String personId, final Person person)
{
// Validate, perform -me- substitution and canonicalize the person ID.
@@ -737,4 +760,71 @@ public class PeopleImpl implements People
{
return authorityService.isAdminAuthority(authorityName);
}
@Override
public void requestPasswordReset(String userId, String client)
{
// Validate the userId and the client
checkRequiredField("userId", userId);
checkRequiredField("client", client);
// This is an un-authenticated API call so we wrap it to run as Admin
AuthenticationUtil.runAs(() -> {
try
{
resetPasswordService.requestReset(userId, client);
}
catch (ResetPasswordWorkflowInvalidUserException ex)
{
// we don't throw an exception.
// For security reason (prevent the attackers to determine that userId exists in the system or not),
// the endpoint returns a 202 response if the userId does not exist or
// if the user is disabled by an Administrator.
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Invalid user. " + ex.getMessage());
}
}
return null;
}, AuthenticationUtil.getAdminUserName());
}
@Override
public void resetPassword(String personId, final PasswordReset passwordReset)
{
checkResetPasswordData(passwordReset);
checkRequiredField("personId", personId);
ResetPasswordDetails resetDetails = new ResetPasswordDetails()
.setUserId(personId)
.setPassword(passwordReset.getPassword())
.setWorkflowId(passwordReset.getId())
.setWorkflowKey(passwordReset.getKey());
try
{
// This is an un-authenticated API call so we wrap it to run as Admin
AuthenticationUtil.runAs(() -> {
resetPasswordService.resetPassword(resetDetails);
return null;
}, AuthenticationUtil.getAdminUserName());
}
catch (InvalidResetPasswordWorkflowException iex)
{
throw new InvalidArgumentException(iex.getMessage());
}
catch (ResetPasswordWorkflowNotFoundException ex)
{
throw new NotFoundException(ex.getMessage());
}
}
private void checkResetPasswordData(PasswordReset data)
{
checkRequiredField("password", data.getPassword());
checkRequiredField("id", data.getId());
checkRequiredField("key", data.getKey());
}
}

View File

@@ -0,0 +1,60 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.model;
/**
* Representation of a client app.
*
* @author Jamal Kaabi-Mofrad
* @since 5.2.1
*/
public class Client
{
/**
* client app name. Used to lookup the client
* that is registered to send emails so that
* client's specific configuration could be used.
*/
protected String client;
public String getClient()
{
return client;
}
public Client setClient(String client)
{
this.client = client;
return this;
}
@Override
public String toString()
{
return "Client [ client=" + client + ']';
}
}

View File

@@ -0,0 +1,91 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.api.model;
/**
* Representation of a password reset.
*
* @author Jamal Kaabi-Mofrad
* @since 5.2.1
*/
public class PasswordReset
{
/** new password */
private String password;
/** workflow Id */
private String id;
/** workflow Key */
private String key;
public PasswordReset()
{
}
public String getPassword()
{
return password;
}
public PasswordReset setPassword(String password)
{
this.password = password;
return this;
}
public String getId()
{
return id;
}
public PasswordReset setId(String id)
{
this.id = id;
return this;
}
public String getKey()
{
return key;
}
public PasswordReset setKey(String key)
{
this.key = key;
return this;
}
@Override
public String toString()
{
// we don't return the password for the obvious reason
final StringBuilder sb = new StringBuilder(100);
sb.append("PasswordReset [id=").append(id)
.append(", key=").append(key)
.append(']');
return sb.toString();
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -27,8 +27,12 @@ package org.alfresco.rest.api.people;
import org.alfresco.model.ContentModel;
import org.alfresco.rest.api.People;
import org.alfresco.rest.api.model.Client;
import org.alfresco.rest.api.model.PasswordReset;
import org.alfresco.rest.api.model.Person;
import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiNoAuth;
import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.core.ResourceParameter;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
@@ -36,11 +40,13 @@ import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.util.ParameterCheck;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
@@ -57,17 +63,17 @@ public class PeopleEntityResource implements EntityResourceAction.ReadById<Perso
private People people;
public void setPeople(People people)
{
this.people = people;
}
public void setPeople(People people)
{
this.people = people;
}
@Override
@Override
public void afterPropertiesSet()
{
ParameterCheck.mandatory("people", this.people);
PropertyCheck.mandatory(this, "people", people);
}
/**
* Get a person by userName.
*
@@ -146,4 +152,21 @@ public class PeopleEntityResource implements EntityResourceAction.ReadById<Perso
{
return people.getPeople(params);
}
@Operation("request-password-reset")
@WebApiDescription(title = "Request Password Reset", description = "Request password reset",
successStatus = HttpServletResponse.SC_ACCEPTED)
@WebApiNoAuth
public void requestPasswordReset(String personId, Client client, Parameters parameters, WithResponse withResponse)
{
people.requestPasswordReset(personId, client.getClient());
}
@Operation("reset-password")
@WebApiDescription(title = "Reset Password", description = "Performs password reset", successStatus = HttpServletResponse.SC_ACCEPTED)
@WebApiNoAuth
public void resetPassword(String personId, PasswordReset passwordReset, Parameters parameters, WithResponse withResponse)
{
people.resetPassword(personId, passwordReset);
}
}