MIFOSX-751 password reset functionality added by Musoni

This commit is contained in:
CieyouRaoul
2014-02-10 04:04:16 +05:30
committed by Vishwas Babu A J
parent 36fcb9c069
commit fe510e8e1a
28 changed files with 708 additions and 77 deletions
+21 -11
View File
@@ -10225,9 +10225,17 @@ GET https://DomainName/api/v1/configurations
{ {
"globalConfiguration": [ "globalConfiguration": [
{ {
"name": "maker-checker", "name": "maker-checker",
"enabled": false "enabled": true,
} "value": 0,
"id": 1
},
{
"name": "amazon-S3",
"enabled": false,
"value": 0,
"id": 2
},
] ]
} }
</code> </code>
@@ -10242,22 +10250,24 @@ GET https://DomainName/api/v1/configurations
</div> </div>
<div class="method-example"> <div class="method-example">
<code class="method-declaration"> <code class="method-declaration">
PUT https://DomainName/api/v1/configurations?maker-checker=false PUT https://DomainName/api/v1/configurations/9
</code> </code>
<code class="method-request"> <code class="method-request">
PUT configurations PUT configurations
Content-Type: application/json Content-Type: application/json
Request Body: { } Request Body: {
"enabled":"true",
"value":2
}
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"changes": { "resourceId": 9,
"globalConfiguration": { "changes": {
"maker-checker": false "enabled": true
} }
} } </code>
}
</code>
</div> </div>
</div> </div>
@@ -0,0 +1,17 @@
INSERT INTO `c_configuration` (`id`, `name`, `value`, `enabled`) VALUES (NULL, 'force-password-reset-days', '0', '0');
ALTER TABLE `m_appuser` ADD `last_time_password_updated` DATE NOT NULL ,
ADD INDEX ( `last_time_password_updated` ) ;
UPDATE `m_appuser` SET `last_time_password_updated` = NOW() WHERE `m_appuser`.`last_time_password_updated` ='0000-00-00';
CREATE TABLE IF NOT EXISTS `m_appuser_previews_passwords` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`password` varchar(255) NOT NULL,
`removal_date` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
ALTER TABLE m_appuser_previews_passwords
ADD FOREIGN KEY (user_id) REFERENCES m_appuser(id);
@@ -54,11 +54,12 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder updateGlobalConfiguration() { public CommandWrapperBuilder updateGlobalConfiguration(final Long configId) {
this.actionName = "UPDATE"; this.actionName = "UPDATE";
this.entityName = "CONFIGURATION"; this.entityName = "CONFIGURATION";
this.entityId = null; this.entityId = configId;
this.href = "/configurations";
this.href = "/configurations/"+configId;
return this; return this;
} }
@@ -47,7 +47,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio
boolean isApprovedByChecker = false; boolean isApprovedByChecker = false;
// check if is update of own account details // check if is update of own account details
if (wrapper.isUpdateOfOwnUserDetails(this.context.authenticatedUser().getId())) { if (wrapper.isUpdateOfOwnUserDetails(this.context.authenticatedUser(wrapper).getId())) {
// then allow this operation to proceed. // then allow this operation to proceed.
// maker checker doesnt mean anything here. // maker checker doesnt mean anything here.
isApprovedByChecker = true; // set to true in case permissions have isApprovedByChecker = true; // set to true in case permissions have
@@ -56,7 +56,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio
} else { } else {
// if not user changing their own details - check user has // if not user changing their own details - check user has
// permission to perform specific task. // permission to perform specific task.
this.context.authenticatedUser().validateHasPermissionTo(wrapper.getTaskPermissionName()); this.context.authenticatedUser(wrapper).validateHasPermissionTo(wrapper.getTaskPermissionName());
} }
validateIsUpdateAllowed(); validateIsUpdateAllowed();
@@ -72,6 +72,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio
result = this.processAndLogCommandService.processAndLogCommand(wrapper, command, isApprovedByChecker); result = this.processAndLogCommandService.processAndLogCommand(wrapper, command, isApprovedByChecker);
} catch (final RollbackTransactionAsCommandIsNotApprovedByCheckerException e) { } catch (final RollbackTransactionAsCommandIsNotApprovedByCheckerException e) {
result = this.processAndLogCommandService.logCommand(e.getCommandSourceResult()); result = this.processAndLogCommandService.logCommand(e.getCommandSourceResult());
} }
@@ -59,7 +59,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
final NewCommandSourceHandler handler = findCommandHandler(wrapper); final NewCommandSourceHandler handler = findCommandHandler(wrapper);
final CommandProcessingResult result = handler.processCommand(command); final CommandProcessingResult result = handler.processCommand(command);
final AppUser maker = this.context.authenticatedUser(); final AppUser maker = this.context.authenticatedUser(wrapper);
CommandSource commandSourceResult = null; CommandSource commandSourceResult = null;
if (command.commandId() != null) { if (command.commandId() != null) {
@@ -0,0 +1,22 @@
package org.mifosplatform.infrastructure.configuration.api;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* Created by Cieyou on 1/16/14.
*/
public class GlobalConfigurationApiConstant {
public static final String enabled = "enabled";
public static final String value = "value";
public static final String id ="id";
public static final String CONFIGURATION_RESOURCE_NAME = "globalConfiguration";
public static final Set<String> UPDATE_CONFIGURATION_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(enabled,
value));
}
@@ -11,12 +11,9 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.ws.rs.Consumes; import javax.ws.rs.*;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
@@ -76,25 +73,21 @@ public class GlobalConfigurationApiResource {
return this.toApiJsonSerializer.serialize(settings, configurationData, this.RESPONSE_DATA_PARAMETERS); return this.toApiJsonSerializer.serialize(settings, configurationData, this.RESPONSE_DATA_PARAMETERS);
} }
@PUT @PUT
@Path("{configId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String updateConfiguration(@Context final UriInfo uriInfo) { public String updateConfiguration(@PathParam("configId") final Long configId,final String apiRequestBodyAsJson) {
final Map<String, String> configurationParameters = ApiParameterHelper.asMap(uriInfo.getQueryParameters());
final Map<String, Object> globalConfiguration = new HashMap<String, Object>();
globalConfiguration.put("globalConfiguration", configurationParameters);
final String apiRequestJson = this.toApiJsonSerializer.serialize(globalConfiguration);
final CommandWrapper commandRequest = new CommandWrapperBuilder() // final CommandWrapper commandRequest = new CommandWrapperBuilder() //
.updateGlobalConfiguration() // .updateGlobalConfiguration(configId) //
.withJson(apiRequestJson) // .withJson(apiRequestBodyAsJson) //
.build(); .build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result); return this.toApiJsonSerializer.serialize(result);
} }
} }
@@ -0,0 +1,67 @@
package org.mifosplatform.infrastructure.configuration.data;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.mifosplatform.infrastructure.configuration.api.GlobalConfigurationApiConstant.*;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.springframework.stereotype.Component;
/**
* Created by Cieyou on 1/16/14.
*/
@Component
public class GlobalConfigurationDataValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public GlobalConfigurationDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForUpdate(final JsonCommand command) {
final String json = command.json();
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,UPDATE_CONFIGURATION_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(CONFIGURATION_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final JsonObject jsonElement = element.getAsJsonObject();
if(this.fromApiJsonHelper.parameterExists(enabled, element))
{
final boolean enabledBool = this.fromApiJsonHelper.extractBooleanNamed(enabled,element);
baseDataValidator.reset().parameter(enabled).value(enabledBool).validateForBooleanValue();
}
if(this.fromApiJsonHelper.parameterExists(value, element))
{
final Long valueStr = this.fromApiJsonHelper.extractLongNamed(value,element);
baseDataValidator.reset().parameter(enabled).value(valueStr).zeroOrPositiveAmount();
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -16,10 +16,20 @@ public class GlobalConfigurationPropertyData {
private final boolean enabled; private final boolean enabled;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Long value; private final Long value;
@SuppressWarnings("unused")
private final Long id;
public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value) { public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value) {
this.name = name; this.name = name;
this.enabled = enabled; this.enabled = enabled;
this.value = value; this.value = value;
this.id = null;
}
public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value, final Long id) {
this.name = name;
this.enabled = enabled;
this.value = value;
this.id = id;
} }
} }
@@ -28,4 +28,8 @@ public interface ConfigurationDomainService {
void updateCache(CacheType cacheType); void updateCache(CacheType cacheType);
Long retrievePenaltyWaitPeriod(); Long retrievePenaltyWaitPeriod();
boolean isPasswordForcedResetEnable();
Long retrievePasswordLiveTime();
} }
@@ -120,4 +120,24 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(propertyName); } if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(propertyName); }
return property.getValue(); return property.getValue();
} }
@Override
public boolean isPasswordForcedResetEnable(){
final String propertyName ="force-password-reset-days";
final GlobalConfigurationProperty property= this.globalConfigurationRepository.findOneByName(propertyName);
if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(propertyName); }
return property.isEnabled();
}
@Override
public Long retrievePasswordLiveTime(){
final String propertyName ="force-password-reset-days";
final GlobalConfigurationProperty property= this.globalConfigurationRepository.findOneByName(propertyName);
if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(propertyName); }
return property.getValue();
}
} }
@@ -5,11 +5,18 @@
*/ */
package org.mifosplatform.infrastructure.configuration.domain; package org.mifosplatform.infrastructure.configuration.domain;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.AbstractPersistable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Entity @Entity
@Table(name = "c_configuration") @Table(name = "c_configuration")
@@ -22,7 +29,7 @@ public class GlobalConfigurationProperty extends AbstractPersistable<Long> {
private boolean enabled; private boolean enabled;
@Column(name= "value", nullable= true) @Column(name= "value", nullable= true)
private final Long value; private Long value;
protected GlobalConfigurationProperty() { protected GlobalConfigurationProperty() {
this.name = null; this.name = null;
@@ -50,4 +57,32 @@ public class GlobalConfigurationProperty extends AbstractPersistable<Long> {
return updated; return updated;
} }
public Map<String, Object> update(final JsonCommand command)
{
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(7);
// final String localeAsInput = command.locale();
// final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
// final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("c");
final String enabledParamName = "enabled";
if (command.isChangeInBooleanParameterNamed(enabledParamName, this.enabled)) {
final Boolean newValue = command.booleanPrimitiveValueOfParameterNamed (enabledParamName);
actualChanges.put(enabledParamName, newValue);
this.enabled = newValue;
}
final String valueParamName = "value";
if (command.isChangeInLongParameterNamed(valueParamName, this.value)) {
final Long newValue = command.longValueOfParameterNamed(valueParamName);
actualChanges.put(valueParamName, newValue);
this.value = newValue;
}
return actualChanges;
}
} }
@@ -16,4 +16,8 @@ public class GlobalConfigurationPropertyNotFoundException extends AbstractPlatfo
public GlobalConfigurationPropertyNotFoundException(final String propertyName) { public GlobalConfigurationPropertyNotFoundException(final String propertyName) {
super("error.msg.configuration.property.invalid", "Configuration property `" + propertyName + "` does not exist", propertyName); super("error.msg.configuration.property.invalid", "Configuration property `" + propertyName + "` does not exist", propertyName);
} }
public GlobalConfigurationPropertyNotFoundException(final Long configId) {
super("error.msg.configuration.id.invalid", "Configuration identifier `" + configId + "` does not exist", configId);
}
} }
@@ -27,6 +27,6 @@ public class UpdateGlobalConfigurationCommandHandler implements NewCommandSource
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.update(command); return this.writePlatformService.update(command.entityId(),command);
} }
} }
@@ -38,7 +38,7 @@ public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPl
this.context.authenticatedUser(); this.context.authenticatedUser();
final String sql = "SELECT c.name, c.enabled, c.value FROM c_configuration c order by c.id"; final String sql = "SELECT c.id, c.name, c.enabled, c.value FROM c_configuration c order by c.id";
final List<GlobalConfigurationPropertyData> globalConfiguration = this.jdbcTemplate.query(sql, this.rm, new Object[] {}); final List<GlobalConfigurationPropertyData> globalConfiguration = this.jdbcTemplate.query(sql, this.rm, new Object[] {});
return new GlobalConfigurationData(globalConfiguration); return new GlobalConfigurationData(globalConfiguration);
@@ -52,8 +52,11 @@ public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPl
final String name = rs.getString("name"); final String name = rs.getString("name");
final boolean enabled = rs.getBoolean("enabled"); final boolean enabled = rs.getBoolean("enabled");
final Long value = rs.getLong("value"); final Long value = rs.getLong("value");
final Long id = rs.getLong("id");
return new GlobalConfigurationPropertyData(name, enabled,value); return new GlobalConfigurationPropertyData(name, enabled,value,id);
} }
} }
} }
@@ -10,5 +10,7 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface GlobalConfigurationWritePlatformService { public interface GlobalConfigurationWritePlatformService {
CommandProcessingResult update(JsonCommand command); //CommandProcessingResult update(JsonCommand command);
CommandProcessingResult update(Long configId, JsonCommand command);
} }
@@ -16,61 +16,114 @@ import org.mifosplatform.infrastructure.configuration.serialization.GlobalConfig
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.mifosplatform.infrastructure.configuration.data.GlobalConfigurationDataValidator;
import org.springframework.dao.DataIntegrityViolationException;
@Service @Service
public class GlobalConfigurationWritePlatformServiceJpaRepositoryImpl implements GlobalConfigurationWritePlatformService { public class GlobalConfigurationWritePlatformServiceJpaRepositoryImpl implements GlobalConfigurationWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(GlobalConfigurationWritePlatformServiceJpaRepositoryImpl.class);
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final GlobalConfigurationRepository repository; private final GlobalConfigurationRepository repository;
private final GlobalConfigurationCommandFromApiJsonDeserializer fromApiJsonDeserializer; private final GlobalConfigurationCommandFromApiJsonDeserializer fromApiJsonDeserializer;
private final GlobalConfigurationDataValidator globalConfigurationDataValidator ;
@Autowired @Autowired
public GlobalConfigurationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, public GlobalConfigurationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final GlobalConfigurationRepository codeRepository, final GlobalConfigurationRepository codeRepository,
final GlobalConfigurationCommandFromApiJsonDeserializer fromApiJsonDeserializer) { final GlobalConfigurationCommandFromApiJsonDeserializer fromApiJsonDeserializer,
final GlobalConfigurationDataValidator dataValidator) {
this.context = context; this.context = context;
this.repository = codeRepository; this.repository = codeRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.globalConfigurationDataValidator = dataValidator;
} }
// @Transactional
// @Override
// public CommandProcessingResult update(final JsonCommand command) {
//
// this.context.authenticatedUser();
//
// final UpdateGlobalConfigurationCommand configurationCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
//
// final Map<String, Boolean> params = configurationCommand.getGlobalConfiguration();
//
// final Map<String, Object> changes = new LinkedHashMap<String, Object>(params.size());
// final Map<String, Boolean> propertiesMap = new LinkedHashMap<String, Boolean>(1);
// for (final String propertyName : params.keySet()) {
// final GlobalConfigurationProperty property = retrieveBy(propertyName);
//
// final Boolean value = params.get(propertyName);
// final boolean updated = property.updateTo(value);
//
// if (updated) {
// propertiesMap.put(propertyName, value);
// this.repository.save(property);
// }
// }
//
// if (!propertiesMap.isEmpty()) {
// changes.put("globalConfiguration", propertiesMap);
// }
//
// return new CommandProcessingResultBuilder().withCommandId(command.commandId()).with(changes).build();
// }
@Transactional @Transactional
@Override @Override
public CommandProcessingResult update(final JsonCommand command) { public CommandProcessingResult update(final Long configId , final JsonCommand command) {
this.context.authenticatedUser(); this.context.authenticatedUser();
final UpdateGlobalConfigurationCommand configurationCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json()); try {
this.globalConfigurationDataValidator.validateForUpdate(command);
final Map<String, Boolean> params = configurationCommand.getGlobalConfiguration(); final GlobalConfigurationProperty configItemForUpdate = this.repository.findOne(configId);
final Map<String, Object> changes = new LinkedHashMap<String, Object>(params.size()); if (configItemForUpdate == null) {
final Map<String, Boolean> propertiesMap = new LinkedHashMap<String, Boolean>(1); throw new GlobalConfigurationPropertyNotFoundException(configId);
for (final String propertyName : params.keySet()) {
final GlobalConfigurationProperty property = retrieveBy(propertyName);
final Boolean value = params.get(propertyName);
final boolean updated = property.updateTo(value);
if (updated) {
propertiesMap.put(propertyName, value);
this.repository.save(property);
} }
final Map<String, Object> changes = configItemForUpdate.update(command);
if (!changes.isEmpty()) {
this.repository.save(configItemForUpdate);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(configId).with(changes).build();
} catch (final DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
} }
if (!propertiesMap.isEmpty()) { }
changes.put("globalConfiguration", propertiesMap);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).with(changes).build();
}
private GlobalConfigurationProperty retrieveBy(final String propertyName) { private GlobalConfigurationProperty retrieveBy(final String propertyName) {
final GlobalConfigurationProperty property = this.repository.findOneByName(propertyName); final GlobalConfigurationProperty property = this.repository.findOneByName(propertyName);
if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(propertyName); } if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(propertyName); }
return property; return property;
} }
/*
* Guaranteed to throw an exception no matter what the data integrity issue
* is.
*/
private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
final Throwable realCause = dve.getMostSpecificCause();
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.globalConfiguration.unknown.data.integrity.issue",
"Unknown data integrity issue with resource: " + realCause.getMessage());
}
} }
@@ -18,9 +18,13 @@ import javax.ws.rs.core.MediaType;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.data.AuthenticatedUserData; import org.mifosplatform.infrastructure.security.data.AuthenticatedUserData;
import org.mifosplatform.infrastructure.security.exception.ResetPasswordException;
import org.mifosplatform.infrastructure.security.service.SpringSecurityPlatformSecurityContext;
import org.mifosplatform.useradministration.data.RoleData; import org.mifosplatform.useradministration.data.RoleData;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
import org.mifosplatform.useradministration.domain.Role; import org.mifosplatform.useradministration.domain.Role;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
@@ -39,13 +43,17 @@ public class AuthenticationApiResource {
private final DaoAuthenticationProvider customAuthenticationProvider; private final DaoAuthenticationProvider customAuthenticationProvider;
private final ToApiJsonSerializer<AuthenticatedUserData> apiJsonSerializerService; private final ToApiJsonSerializer<AuthenticatedUserData> apiJsonSerializerService;
private final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext;
private final static Logger logger = LoggerFactory.getLogger(AuthenticationApiResource.class);
@Autowired @Autowired
public AuthenticationApiResource( public AuthenticationApiResource(
@Qualifier("customAuthenticationProvider") final DaoAuthenticationProvider customAuthenticationProvider, @Qualifier("customAuthenticationProvider") final DaoAuthenticationProvider customAuthenticationProvider,
final ToApiJsonSerializer<AuthenticatedUserData> apiJsonSerializerService) { final ToApiJsonSerializer<AuthenticatedUserData> apiJsonSerializerService,final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext) {
this.customAuthenticationProvider = customAuthenticationProvider; this.customAuthenticationProvider = customAuthenticationProvider;
this.apiJsonSerializerService = apiJsonSerializerService; this.apiJsonSerializerService = apiJsonSerializerService;
this.springSecurityPlatformSecurityContext = springSecurityPlatformSecurityContext;
} }
@POST @POST
@@ -72,6 +80,8 @@ public class AuthenticationApiResource {
roles.add(role.toData()); roles.add(role.toData());
} }
final Long officeId = principal.getOffice().getId(); final Long officeId = principal.getOffice().getId();
final String officeName = principal.getOffice().getName(); final String officeName = principal.getOffice().getName();
@@ -80,8 +90,17 @@ public class AuthenticationApiResource {
final EnumOptionData organisationalRole = principal.organisationalRoleData(); final EnumOptionData organisationalRole = principal.organisationalRoleData();
authenticatedUserData = new AuthenticatedUserData(username, officeId, officeName, staffId, staffDisplayName, if(this.springSecurityPlatformSecurityContext.doesPasswordHasToBeRenewed(principal)){
organisationalRole, roles, permissions, principal.getId(), new String(base64EncodedAuthenticationKey)); //throw new ResetPasswordException(principal.getId());
authenticatedUserData = new AuthenticatedUserData(username,principal.getId(), new String(base64EncodedAuthenticationKey));
}
else{
authenticatedUserData = new AuthenticatedUserData(username, officeId, officeName, staffId, staffDisplayName,
organisationalRole, roles, permissions, principal.getId(), new String(base64EncodedAuthenticationKey));
}
} }
return this.apiJsonSerializerService.serialize(authenticatedUserData); return this.apiJsonSerializerService.serialize(authenticatedUserData);
@@ -38,6 +38,9 @@ public class AuthenticatedUserData {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Collection<String> permissions; private final Collection<String> permissions;
@SuppressWarnings("unused")
private final boolean shouldRenewPassword;
public AuthenticatedUserData(final String username, final Collection<String> permissions) { public AuthenticatedUserData(final String username, final Collection<String> permissions) {
this.username = username; this.username = username;
this.userId = null; this.userId = null;
@@ -50,6 +53,7 @@ public class AuthenticatedUserData {
this.organisationalRole = null; this.organisationalRole = null;
this.roles = null; this.roles = null;
this.permissions = permissions; this.permissions = permissions;
this.shouldRenewPassword = false;
} }
public AuthenticatedUserData(final String username, final Long officeId, final String officeName, final Long staffId, public AuthenticatedUserData(final String username, final Long officeId, final String officeName, final Long staffId,
@@ -66,5 +70,21 @@ public class AuthenticatedUserData {
this.authenticated = true; this.authenticated = true;
this.roles = roles; this.roles = roles;
this.permissions = permissions; this.permissions = permissions;
this.shouldRenewPassword = false;
}
public AuthenticatedUserData(final String username, final Long userId, final String base64EncodedAuthenticationKey) {
this.username = username;
this.officeId = null;
this.officeName = null;
this.staffId = null;
this.staffDisplayName = null;
this.organisationalRole = null;
this.userId = userId;
this.base64EncodedAuthenticationKey = base64EncodedAuthenticationKey;
this.authenticated = true;
this.roles = null;
this.permissions = null;
this.shouldRenewPassword = true;
} }
} }
@@ -0,0 +1,44 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.infrastructure.security.exception;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformServiceUnavailableException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import java.util.ArrayList;
import java.util.List;
/**
* A {@link RuntimeException} that is thrown in the case where a user does not
* have sufficient authorization to execute operation on platform.
*/
public class ResetPasswordException extends PlatformApiDataValidationException {
public ResetPasswordException(final Long userId) {
super(
"error.msg.password.outdated",
"The password of the user with id "+userId+" has expired, please reset it it",
new ArrayList<ApiParameterError>(){
{
add (ApiParameterError.parameterError(
"error.msg.password.outdated",
"The password of the user with id "+userId+" has expired, please reset it it",
"userId",userId)
);
}
}
);
}
}
@@ -5,6 +5,8 @@
*/ */
package org.mifosplatform.infrastructure.security.service; package org.mifosplatform.infrastructure.security.service;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
public interface PlatformSecurityContext { public interface PlatformSecurityContext {
@@ -14,4 +16,8 @@ public interface PlatformSecurityContext {
void validateAccessRights(String resourceOfficeHierarchy); void validateAccessRights(String resourceOfficeHierarchy);
String officeHierarchy(); String officeHierarchy();
boolean doesPasswordHasToBeRenewed(AppUser currentUser);
AppUser authenticatedUser(CommandWrapper commandWrapper);
} }
@@ -5,23 +5,56 @@
*/ */
package org.mifosplatform.infrastructure.security.service; package org.mifosplatform.infrastructure.security.service;
import com.sun.jersey.api.spring.Autowire;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.security.exception.NoAuthorizationException; import org.mifosplatform.infrastructure.security.exception.NoAuthorizationException;
import org.mifosplatform.infrastructure.security.exception.ResetPasswordException;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
import org.mifosplatform.useradministration.exception.UnAuthenticatedUserException; import org.mifosplatform.useradministration.exception.UnAuthenticatedUserException;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainServiceJpa;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import sun.print.resources.serviceui_zh_TW;
import java.lang.reflect.Array;
import java.util.*;
/** /**
* Wrapper around spring security's {@link SecurityContext} for extracted the * Wrapper around spring security's {@link SecurityContext} for extracted the
* current authenticated {@link AppUser}. * current authenticated {@link AppUser}.
*/ */
@Service @Service
public class SpringSecurityPlatformSecurityContext implements PlatformSecurityContext { public class SpringSecurityPlatformSecurityContext implements PlatformSecurityContext {
private final ConfigurationDomainService configurationDomainService ;
private final static Logger logger = LoggerFactory.getLogger(SpringSecurityPlatformSecurityContext.class);
public static final List<CommandWrapper> EXEMPT_FROM_PASSWORD_RESET_CHECK = new ArrayList<CommandWrapper>(){
{
add(new CommandWrapperBuilder().updateUser(null).build());
}
};
@Autowired
SpringSecurityPlatformSecurityContext(final ConfigurationDomainService configurationDomainService){
this.configurationDomainService = configurationDomainService;
}
@Override @Override
public AppUser authenticatedUser() { public AppUser authenticatedUser() {
AppUser currentUser = null; AppUser currentUser = null;
final SecurityContext context = SecurityContextHolder.getContext(); final SecurityContext context = SecurityContextHolder.getContext();
if (context != null) { if (context != null) {
@@ -33,9 +66,34 @@ public class SpringSecurityPlatformSecurityContext implements PlatformSecurityCo
if (currentUser == null) { throw new UnAuthenticatedUserException(); } if (currentUser == null) { throw new UnAuthenticatedUserException(); }
if(this.doesPasswordHasToBeRenewed(currentUser)) { throw new ResetPasswordException(currentUser.getId());}
return currentUser; return currentUser;
} }
public AppUser authenticatedUser(CommandWrapper commandWrapper)
{
AppUser currentUser = null;
final SecurityContext context = SecurityContextHolder.getContext();
if (context != null) {
final Authentication auth = context.getAuthentication();
if (auth != null) {
currentUser = (AppUser) auth.getPrincipal();
}
}
if (currentUser == null) { throw new UnAuthenticatedUserException(); }
if(this.shouldCheckForPasswordForceReset(commandWrapper) && this.doesPasswordHasToBeRenewed(currentUser)) { throw new ResetPasswordException(currentUser.getId());}
return currentUser;
}
@Override @Override
public void validateAccessRights(final String resourceOfficeHierarchy) { public void validateAccessRights(final String resourceOfficeHierarchy) {
@@ -51,4 +109,51 @@ public class SpringSecurityPlatformSecurityContext implements PlatformSecurityCo
public String officeHierarchy() { public String officeHierarchy() {
return authenticatedUser().getOffice().getHierarchy(); return authenticatedUser().getOffice().getHierarchy();
} }
@Override
public boolean doesPasswordHasToBeRenewed(AppUser currentUser )
{
if( this.configurationDomainService.isPasswordForcedResetEnable()){
Long passwordDurationDays = this.configurationDomainService.retrievePasswordLiveTime();
final Date passWordLastUpdateDate = currentUser .getLastTimePasswordUpdated();
Calendar c = Calendar.getInstance();
c.setTime(passWordLastUpdateDate);
c.add(Calendar.DATE,passwordDurationDays.intValue());
final Date passwordExpirationDate = c.getTime();
if(DateUtils.getDateOfTenant().after(passwordExpirationDate)){
return true ;
}
else { return false; }
}
else{
return false;
}
}
private boolean shouldCheckForPasswordForceReset(CommandWrapper commandWrapper)
{
for( CommandWrapper commandItem : this.EXEMPT_FROM_PASSWORD_RESET_CHECK )
{
if(commandItem.actionName().equals(commandWrapper.actionName())
&& commandItem.getEntityName().equals(commandWrapper.getEntityName()))
{
return false;
}
}
return true;
}
} }
@@ -0,0 +1,14 @@
package org.mifosplatform.useradministration.api;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* Created by Cieyou on 1/16/14.
*/
public class AppUserApiConstant {
public static final int numberOfPreviousPasswords = 3;
}
@@ -12,16 +12,9 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Date;
import javax.persistence.Column; import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
@@ -37,6 +30,8 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User;
import org.mifosplatform.infrastructure.core.service.DateUtils;
@Entity @Entity
@Table(name = "m_appuser", uniqueConstraints = @UniqueConstraint(columnNames = { "username" }, name = "username_org")) @Table(name = "m_appuser", uniqueConstraints = @UniqueConstraint(columnNames = { "username" }, name = "username_org"))
@@ -89,6 +84,10 @@ public class AppUser extends AbstractPersistable<Long> implements PlatformUser {
@JoinTable(name = "m_appuser_role", joinColumns = @JoinColumn(name = "appuser_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) @JoinTable(name = "m_appuser_role", joinColumns = @JoinColumn(name = "appuser_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles; private Set<Role> roles;
@Column(name = "last_time_password_updated")
@Temporal(TemporalType.DATE)
private Date lastTimePasswordUpdated;
public static AppUser fromJson(final Office userOffice, final Set<Role> allRoles, final JsonCommand command) { public static AppUser fromJson(final Office userOffice, final Set<Role> allRoles, final JsonCommand command) {
final String username = command.stringValueOfParameterNamed("username"); final String username = command.stringValueOfParameterNamed("username");
@@ -137,6 +136,7 @@ public class AppUser extends AbstractPersistable<Long> implements PlatformUser {
this.enabled = user.isEnabled(); this.enabled = user.isEnabled();
this.roles = roles; this.roles = roles;
this.firstTimeLoginRemaining = true; this.firstTimeLoginRemaining = true;
this.lastTimePasswordUpdated = DateUtils.getDateOfTenant();
} }
public EnumOptionData organisationalRoleData() { public EnumOptionData organisationalRoleData() {
@@ -150,6 +150,8 @@ public class AppUser extends AbstractPersistable<Long> implements PlatformUser {
public void updatePassword(final String encodePassword) { public void updatePassword(final String encodePassword) {
this.password = encodePassword; this.password = encodePassword;
this.firstTimeLoginRemaining = false; this.firstTimeLoginRemaining = false;
this.lastTimePasswordUpdated = DateUtils.getDateOfTenant();
} }
public void changeOffice(final Office differentOffice) { public void changeOffice(final Office differentOffice) {
@@ -324,6 +326,8 @@ public class AppUser extends AbstractPersistable<Long> implements PlatformUser {
return this.office; return this.office;
} }
public Date getLastTimePasswordUpdated(){ return this.lastTimePasswordUpdated; }
public boolean canNotApproveLoanInPast() { public boolean canNotApproveLoanInPast() {
return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "APPROVEINPAST_LOAN"); return hasNotPermissionForAnyOf("ALL_FUNCTIONS", "APPROVEINPAST_LOAN");
} }
@@ -491,4 +495,31 @@ public class AppUser extends AbstractPersistable<Long> implements PlatformUser {
} }
return staffDisplayName; return staffDisplayName;
} }
public String getEncodedPassword(final JsonCommand command,final PlatformPasswordEncoder platformPasswordEncoder)
{
final String passwordParamName = "password";
final String passwordEncodedParamName = "passwordEncoded";
String passwordEncodedValue = null;
if (command.hasParameter(passwordParamName)) {
if (command.isChangeInPasswordParameterNamed(passwordParamName, this.password
, platformPasswordEncoder,getId())) {
passwordEncodedValue = command.passwordValueOfParameterNamed(passwordParamName, platformPasswordEncoder,
getId());
}
}
else if (command.hasParameter(passwordEncodedParamName)) {
if (command.isChangeInStringParameterNamed(passwordEncodedParamName, this.password)) {
passwordEncodedValue = command.stringValueOfParameterNamed(passwordEncodedParamName);
}
}
return passwordEncodedValue;
}
} }
@@ -0,0 +1,53 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.useradministration.domain;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.jpa.domain.AbstractPersistable;
import javax.persistence.*;
import java.util.*;
@Entity
@Table(name = "m_appuser_previews_passwords")
public class AppUserPreviewPassword extends AbstractPersistable<Long> {
private final static Logger logger = LoggerFactory.getLogger(AppUserPreviewPassword.class);
@Column(name = "user_id", nullable = false)
private Long userId;
@Column(name = "removal_date")
@Temporal(TemporalType.DATE)
private Date removalDate;
@Column(name = "password", nullable = false)
private String password;
protected AppUserPreviewPassword() {
}
public AppUserPreviewPassword(final AppUser user) {
this.userId = user.getId();
this.password = user.getPassword().trim();
this.removalDate = DateUtils.getDateOfTenant();
}
public String getPassword()
{
return this.password;
}
}
@@ -0,0 +1,24 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.useradministration.domain;
import org.mifosplatform.infrastructure.security.domain.PlatformUserRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface AppUserPreviewPasswordRepository extends JpaRepository<AppUserPreviewPassword, Long>, JpaSpecificationExecutor<AppUserPreviewPassword> {
// no behaviour added
// @Query("SELECT upp FROM AppUserPreviewPassword upp WHERE upp.userId = :userId order by upp.removalDate ASC ")
public List<AppUserPreviewPassword> findByUserId(Long userId,Pageable pageable);
}
@@ -0,0 +1,16 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.useradministration.exception;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
public class PasswordPreviouslyUsedException extends PlatformApiDataValidationException {
public PasswordPreviouslyUsedException() {
super("error.msg.password.already.used", "The submitted password has already been used in the past",null);
}
}
@@ -11,6 +11,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
@@ -23,11 +25,9 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext
import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository; import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.organisation.office.exception.OfficeNotFoundException; import org.mifosplatform.organisation.office.exception.OfficeNotFoundException;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.api.AppUserApiConstant;
import org.mifosplatform.useradministration.domain.AppUserRepository; import org.mifosplatform.useradministration.domain.*;
import org.mifosplatform.useradministration.domain.Role; import org.mifosplatform.useradministration.exception.PasswordPreviouslyUsedException;
import org.mifosplatform.useradministration.domain.RoleRepository;
import org.mifosplatform.useradministration.domain.UserDomainService;
import org.mifosplatform.useradministration.exception.RoleNotFoundException; import org.mifosplatform.useradministration.exception.RoleNotFoundException;
import org.mifosplatform.useradministration.exception.UserNotFoundException; import org.mifosplatform.useradministration.exception.UserNotFoundException;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -36,6 +36,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching; import org.springframework.cache.annotation.Caching;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@@ -52,11 +54,12 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
private final OfficeRepository officeRepository; private final OfficeRepository officeRepository;
private final RoleRepository roleRepository; private final RoleRepository roleRepository;
private final UserDataValidator fromApiJsonDeserializer; private final UserDataValidator fromApiJsonDeserializer;
private final AppUserPreviewPasswordRepository appUserPreviewPasswordRepository;
@Autowired @Autowired
public AppUserWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final AppUserRepository appUserRepository, public AppUserWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final AppUserRepository appUserRepository,
final UserDomainService userDomainService, final OfficeRepository officeRepository, final RoleRepository roleRepository, final UserDomainService userDomainService, final OfficeRepository officeRepository, final RoleRepository roleRepository,
final PlatformPasswordEncoder platformPasswordEncoder, final UserDataValidator fromApiJsonDeserializer) { final PlatformPasswordEncoder platformPasswordEncoder, final UserDataValidator fromApiJsonDeserializer,final AppUserPreviewPasswordRepository appUserPreviewPasswordRepository) {
this.context = context; this.context = context;
this.appUserRepository = appUserRepository; this.appUserRepository = appUserRepository;
this.userDomainService = userDomainService; this.userDomainService = userDomainService;
@@ -64,6 +67,7 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
this.roleRepository = roleRepository; this.roleRepository = roleRepository;
this.platformPasswordEncoder = platformPasswordEncoder; this.platformPasswordEncoder = platformPasswordEncoder;
this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.appUserPreviewPasswordRepository = appUserPreviewPasswordRepository;
} }
@Transactional @Transactional
@@ -120,13 +124,21 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
public CommandProcessingResult updateUser(final Long userId, final JsonCommand command) { public CommandProcessingResult updateUser(final Long userId, final JsonCommand command) {
try { try {
this.context.authenticatedUser();
this.context.authenticatedUser(new CommandWrapperBuilder().updateUser(null).build());
this.fromApiJsonDeserializer.validateForUpdate(command.json()); this.fromApiJsonDeserializer.validateForUpdate(command.json());
final AppUser userToUpdate = this.appUserRepository.findOne(userId); final AppUser userToUpdate = this.appUserRepository.findOne(userId);
if (userToUpdate == null) { throw new UserNotFoundException(userId); } if (userToUpdate == null) { throw new UserNotFoundException(userId); }
final AppUserPreviewPassword currentPasswordToSaveAsPreview = getCurrentPasswordToSaveAsPreview(userToUpdate,command);
final Map<String, Object> changes = userToUpdate.update(command, this.platformPasswordEncoder); final Map<String, Object> changes = userToUpdate.update(command, this.platformPasswordEncoder);
if (changes.containsKey("officeId")) { if (changes.containsKey("officeId")) {
@@ -146,6 +158,9 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.appUserRepository.saveAndFlush(userToUpdate); this.appUserRepository.saveAndFlush(userToUpdate);
if(currentPasswordToSaveAsPreview!=null) {this.appUserPreviewPasswordRepository.save(currentPasswordToSaveAsPreview);}
} }
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
@@ -155,10 +170,52 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
.build(); .build();
} catch (final DataIntegrityViolationException dve) { } catch (final DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve); handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty(); return CommandProcessingResult.empty();
} }
} }
/**
* encode the new submitted password
* retrieve the last n used password
* check if the current submitted password, match with one of them
* @param user
* @param command
* @return
*/
private AppUserPreviewPassword getCurrentPasswordToSaveAsPreview(final AppUser user,final JsonCommand command)
{
final String passWordEncodedValue = user.getEncodedPassword(command,this.platformPasswordEncoder);
AppUserPreviewPassword currentPasswordToSaveAsPreview = null;
if(passWordEncodedValue!=null )
{
PageRequest pageRequest = new PageRequest(0, AppUserApiConstant.numberOfPreviousPasswords, Sort.Direction.DESC,"removalDate");
final List<AppUserPreviewPassword> nLastUsedPasswords = this.appUserPreviewPasswordRepository.findByUserId(user.getId(),pageRequest);
for(AppUserPreviewPassword aPreviewPassword : nLastUsedPasswords)
{
if(aPreviewPassword.getPassword().equals(passWordEncodedValue)){
throw new PasswordPreviouslyUsedException();
}
}
currentPasswordToSaveAsPreview = new AppUserPreviewPassword(user);
}
return currentPasswordToSaveAsPreview ;
}
private Set<Role> assembleSetOfRoles(final String[] rolesArray) { private Set<Role> assembleSetOfRoles(final String[] rolesArray) {
final Set<Role> allRoles = new HashSet<Role>(); final Set<Role> allRoles = new HashSet<Role>();