From d89152ce826a37fbbfc344748882931257e60a97 Mon Sep 17 00:00:00 2001 From: CieyouRaoul Date: Tue, 10 Feb 2015 15:22:35 +0100 Subject: [PATCH] MIFOSX-1875 password validation policy --- api-docs/apiLive.htm | 71 ++++++++++++ .../commands/domain/CommandWrapper.java | 11 ++ .../service/CommandWrapperBuilder.java | 9 +- .../SynchronousCommandProcessingService.java | 17 ++- .../core/data/DataValidatorBuilder.java | 35 ++++-- .../PasswordValidationPolicyApiResource.java | 96 +++++++++++++++ .../data/PasswordValidationPolicyData.java | 27 +++++ .../domain/PasswordValidationPolicy.java | 81 +++++++++++++ .../PasswordValidationPolicyRepository.java | 20 ++++ ...wordValidationPolicyNotFoundException.java | 18 +++ ...asswordValidationPolicyCommandHandler.java | 33 ++++++ ...rdValidationPolicyReadPlatformService.java | 16 +++ ...lidationPolicyReadPlatformServiceImpl.java | 59 ++++++++++ ...dValidationPolicyWritePlatformService.java | 16 +++ ...WritePlatformServiceJpaRepositoryImpl.java | 109 ++++++++++++++++++ .../service/UserDataValidator.java | 20 +++- .../V206_7__password_validation_policy.sql | 31 +++++ ...password_validation_policy_permissions.sql | 7 ++ 18 files changed, 659 insertions(+), 17 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/useradministration/api/PasswordValidationPolicyApiResource.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/useradministration/data/PasswordValidationPolicyData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/PasswordValidationPolicy.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/PasswordValidationPolicyRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/useradministration/exception/PasswordValidationPolicyNotFoundException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/useradministration/handler/UpdatePasswordValidationPolicyCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyReadPlatformServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyWritePlatformServiceJpaRepositoryImpl.java create mode 100644 mifosng-provider/src/main/resources/sql/migrations/core_db/V206_7__password_validation_policy.sql create mode 100644 mifosng-provider/src/main/resources/sql/migrations/core_db/V206_8__password_validation_policy_permissions.sql diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 63d9e8b47..da85384c3 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -2026,6 +2026,14 @@ Enable/Disable Permissions for Maker Checker + + Password Validation Policy + paswordValidationPolicy + + List validation Policy + Activate policy + + @@ -30709,7 +30717,70 @@ Request Body: + +   +
+
+

Password validation policy

+

An API capability to support management of application password validation policy for user administration.

+

There is no Mifos X functionality for creating or validation policy. The validation policies come pre-installed.

+

Validation policies are not updated, except in the case of enabling them

+
+
+   +
+
+

List Application Password validation policies

+
Arguments
+
+ + +
+

Example Requests:

+
passwordValidationPolicy


+
+
+ + GET https://DomainName/api/v1/passwordValidationPolicy + + + [ + { + "id": 1, + "description": "Password most be at least 1 character and not more that 50 characters long", + "active": true + }, + { + "id": 2, + "description": "Password must be at least 6 characters, no more than 50 characters long, must include at least one upper case letter, one lower case letter, one numeric digit and no space", + "active": false + } + ] + +
+
+ +   +
+
+

Enable Password validation policy

+
+
+ + PUT https://DomainName/api/v1/passwordValidationPolicy/{validationPolicyId} + + + PUT passwordValidationPolicy + Content-Type: application/json + Request Body: + { + + } + +
+
 
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index c13f419ff..7249a0123 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -31,6 +31,8 @@ public class CommandWrapper { @SuppressWarnings("unused") private Long templateId; + + public static CommandWrapper wrap(final String actionName, final String entityName, final Long resourceId, final Long subresourceId) { return new CommandWrapper(null, actionName, entityName, resourceId, subresourceId, null, null); } @@ -1008,4 +1010,13 @@ public class CommandWrapper { public boolean isEnableOperation() { return this.actionName.equalsIgnoreCase("ENABLE"); } + + public boolean isPasswordValidationPolicyResource(){ + return this.entityName.equalsIgnoreCase("PASSWORD_VALIDATION_POLICY");} + + + public boolean isActivatePasswordValidationPolicy(){ + return this.actionName.equalsIgnoreCase("UPDATE"); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 6fafeaf2e..9e217bab9 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -2308,7 +2308,6 @@ public class CommandWrapperBuilder { this.json = "{}"; return this; } - public CommandWrapperBuilder disableRole(Long roleId) { this.actionName = "DISABLE"; this.entityName = "ROLE"; @@ -2327,4 +2326,12 @@ public class CommandWrapperBuilder { return this; } + public CommandWrapperBuilder activatePasswordValidationPolicy(final Long validationPolicyId) { + this.actionName = "UPDATE"; + this.entityName = "PASSWORD_VALIDATION_POLICY"; + this.entityId =validationPolicyId ; + this.href = "/passwordValidationPolicy/" + validationPolicyId; + return this; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index 58d182c13..d4428083b 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -5,8 +5,6 @@ */ package org.mifosplatform.commands.service; -import java.util.Map; - import org.joda.time.DateTime; import org.mifosplatform.commands.domain.CommandSource; import org.mifosplatform.commands.domain.CommandSourceRepository; @@ -29,6 +27,8 @@ import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Map; + @Service public class SynchronousCommandProcessingService implements CommandProcessingService { @@ -910,7 +910,18 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer } else { throw new UnsupportedCommandException(wrapper.commandName()); } - } else { + + }else if (wrapper.isPasswordValidationPolicyResource()) { + + if (wrapper.isActivatePasswordValidationPolicy()) { + + handler = this.applicationContext.getBean("updatePasswordValidationPolicyCommandHandler", NewCommandSourceHandler.class); + + } else { + throw new UnsupportedCommandException(wrapper.commandName()); + } + } + else { throw new UnsupportedCommandException(wrapper.commandName()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java index ea35cf403..870ffa111 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java @@ -5,6 +5,14 @@ */ package org.mifosplatform.infrastructure.core.data; +import com.google.gson.JsonArray; +import net.fortuna.ical4j.model.ValidationException; +import net.fortuna.ical4j.model.property.RRule; +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.quartz.CronExpression; +import org.springframework.util.ObjectUtils; + import java.math.BigDecimal; import java.text.ParseException; import java.util.Arrays; @@ -12,16 +20,6 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import net.fortuna.ical4j.model.ValidationException; -import net.fortuna.ical4j.model.property.RRule; - -import org.apache.commons.lang.StringUtils; -import org.joda.time.LocalDate; -import org.quartz.CronExpression; -import org.springframework.util.ObjectUtils; - -import com.google.gson.JsonArray; - public class DataValidatorBuilder { public static final String VALID_INPUT_SEPERATOR = "_"; @@ -725,6 +723,23 @@ public class DataValidatorBuilder { return this; } + public DataValidatorBuilder matchesRegularExpression(final String expression, final String Message) { + if (this.value == null && this.ignoreNullValue) { return this; } + + if (this.value != null && !this.value.toString().matches(expression)) { + final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") + .append(this.parameter).append(".does.not.match.regexp"); + final StringBuilder defaultEnglishMessage = new StringBuilder(Message); + + final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), + defaultEnglishMessage.toString(), this.parameter, this.value, expression); + + this.dataValidationErrors.add(error); + } + + return this; + } + private DataValidatorBuilder validateStringFor(final String validInputs) { if (this.value == null && this.ignoreNullValue) { return this; } final String[] inputs = validInputs.split(VALID_INPUT_SEPERATOR); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/api/PasswordValidationPolicyApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/api/PasswordValidationPolicyApiResource.java new file mode 100644 index 000000000..7ce751ac0 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/api/PasswordValidationPolicyApiResource.java @@ -0,0 +1,96 @@ +/** + * 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.api; + +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.useradministration.data.PasswordValidationPolicyData; +import org.mifosplatform.useradministration.data.RoleData; +import org.mifosplatform.useradministration.data.RolePermissionsData; +import org.mifosplatform.useradministration.service.PasswordValidationPolicyReadPlatformService; +import org.mifosplatform.useradministration.service.PermissionReadPlatformService; +import org.mifosplatform.useradministration.service.RoleReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.ws.rs.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +@Path("/passwordValidationPolicy") +@Component +@Scope("singleton") +public class PasswordValidationPolicyApiResource { + + /** + * The set of parameters that are supported in response for {@link org.mifosplatform.useradministration.data.RoleData} + */ + private final Set RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "description", + "active")); + + + private final String resourceNameForPermissions = "Password_Validation_Policy"; + + private final PlatformSecurityContext context; + private final PasswordValidationPolicyReadPlatformService passwordValidationPolicyReadPlatformService; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + + @Autowired + public PasswordValidationPolicyApiResource(final PlatformSecurityContext context, final PasswordValidationPolicyReadPlatformService readPlatformService, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final ApiRequestParameterHelper apiRequestParameterHelper, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { + this.context = context; + this.passwordValidationPolicyReadPlatformService = readPlatformService; + this.toApiJsonSerializer = toApiJsonSerializer; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAllPolicy(@Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); + + final Collection validationPolicies = this.passwordValidationPolicyReadPlatformService.retrieveAll(); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize( settings,validationPolicies ,this.RESPONSE_DATA_PARAMETERS); + } + + @PUT + @Path("{validationPolicyId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String activate(@PathParam("validationPolicyId") final Long validationPolicyId, final String apiRequestBodyAsJson) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder() // + .activatePasswordValidationPolicy(validationPolicyId) // + .withJson(apiRequestBodyAsJson) // + .build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.toApiJsonSerializer.serialize(result); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/data/PasswordValidationPolicyData.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/data/PasswordValidationPolicyData.java new file mode 100644 index 000000000..978e5b9a9 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/data/PasswordValidationPolicyData.java @@ -0,0 +1,27 @@ +/** + * 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.data; + +import java.io.Serializable; + +/** + * Immutable data object for role data. + */ +public class PasswordValidationPolicyData implements Serializable { + + private final Long id; + private final String description; + private final boolean active; + + + public PasswordValidationPolicyData(final Long id, final Boolean active, final String description) { + this.id = id; + this.active = active; + this.description = description; + } + + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/PasswordValidationPolicy.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/PasswordValidationPolicy.java new file mode 100644 index 000000000..7ce741bc5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/PasswordValidationPolicy.java @@ -0,0 +1,81 @@ +/** + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.jpa.domain.AbstractPersistable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.LinkedHashMap; +import java.util.Map; + +@Entity +@Table(name = "m_password_validation_policy") +public class PasswordValidationPolicy extends AbstractPersistable { + + private final static Logger logger = LoggerFactory.getLogger(PasswordValidationPolicy.class); + + @Column(name = "regex", nullable = false) + private String regex; + + @Column(name = "description", nullable = false) + private String description; + + @Column(name = "active", nullable = false) + private boolean active ; + + public PasswordValidationPolicy(final String regex, final String description, final boolean active ) { + this.description = description; + this.regex = regex; + this.active = active; + } + + public PasswordValidationPolicy(){ + this.active = false; + } + + + public String getDescription() { + return description; + } + + public String getRegex() { + return regex; + } + + public boolean getActive() { + return this.active; + } + + public Map active(){ + final Map actualChanges = new LinkedHashMap<>(1); + + final String active = "active"; + + if(!this.active){ + + actualChanges.put(active, true); + this.active = true; + } + + return actualChanges; + } + + public boolean isActive(){ + return this.active; + } + + public void deActive(){ + this.active = false; + } + + + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/PasswordValidationPolicyRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/PasswordValidationPolicyRepository.java new file mode 100644 index 000000000..01ccec111 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/domain/PasswordValidationPolicyRepository.java @@ -0,0 +1,20 @@ +/** + * 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.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; + +public interface PasswordValidationPolicyRepository extends JpaRepository, + JpaSpecificationExecutor { + + @Query("from PasswordValidationPolicy PVP WHERE PVP.active = 1") + public PasswordValidationPolicy findActivePasswordValidationPolicy(); + + + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/exception/PasswordValidationPolicyNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/exception/PasswordValidationPolicyNotFoundException.java new file mode 100644 index 000000000..0f357e401 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/exception/PasswordValidationPolicyNotFoundException.java @@ -0,0 +1,18 @@ +/** + * 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.AbstractPlatformResourceNotFoundException; + +/** + * A {@link RuntimeException} thrown when role resources are not found. + */ +public class PasswordValidationPolicyNotFoundException extends AbstractPlatformResourceNotFoundException { + + public PasswordValidationPolicyNotFoundException(final Long id) { + super("error.msg.validation.policy.id.invalid", "Password Validation Policy with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/handler/UpdatePasswordValidationPolicyCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/handler/UpdatePasswordValidationPolicyCommandHandler.java new file mode 100644 index 000000000..122bf4427 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/handler/UpdatePasswordValidationPolicyCommandHandler.java @@ -0,0 +1,33 @@ +/** + * 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.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.useradministration.service.PasswordValidationPolicyWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class UpdatePasswordValidationPolicyCommandHandler implements NewCommandSourceHandler { + + private final PasswordValidationPolicyWritePlatformService writePlatformService; + + @Autowired + public UpdatePasswordValidationPolicyCommandHandler(final PasswordValidationPolicyWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + final Long validationPolicyId = command.entityId(); + return this.writePlatformService.activate(validationPolicyId, command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyReadPlatformService.java new file mode 100644 index 000000000..1151c4504 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyReadPlatformService.java @@ -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.service; + +import org.mifosplatform.useradministration.data.PasswordValidationPolicyData; + +import java.util.Collection; + +public interface PasswordValidationPolicyReadPlatformService { + + Collection retrieveAll(); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyReadPlatformServiceImpl.java new file mode 100644 index 000000000..911b6d6fa --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyReadPlatformServiceImpl.java @@ -0,0 +1,59 @@ +/** + * 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.service; + +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.useradministration.data.PasswordValidationPolicyData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +@Service +public class PasswordValidationPolicyReadPlatformServiceImpl implements PasswordValidationPolicyReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final PasswordValidationPolicyMapper passwordValidationPolicyMapper; + + @Autowired + public PasswordValidationPolicyReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.passwordValidationPolicyMapper = new PasswordValidationPolicyMapper(); + } + + @Override + public Collection retrieveAll() { + final String sql = "select " + this.passwordValidationPolicyMapper.schema() + " order by pvp.id"; + + return this.jdbcTemplate.query(sql, this.passwordValidationPolicyMapper); + } + + + + protected static final class PasswordValidationPolicyMapper implements RowMapper { + + @Override + public PasswordValidationPolicyData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = JdbcSupport.getLong(rs, "id"); + final Boolean active = rs.getBoolean("active"); + final String description = rs.getString("description"); + + return new PasswordValidationPolicyData(id, active, description); + } + + public String schema() { + return " pvp.id as id, pvp.active as active, pvp.description as description from m_password_validation_policy pvp"; + } + } + + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyWritePlatformService.java new file mode 100644 index 000000000..7bdf03a94 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyWritePlatformService.java @@ -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.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface PasswordValidationPolicyWritePlatformService { + + + CommandProcessingResult activate(Long validationPolicyId, JsonCommand command); + + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..6463ed26a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/PasswordValidationPolicyWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,109 @@ +/** + * 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.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.useradministration.domain.PasswordValidationPolicy; +import org.mifosplatform.useradministration.domain.PasswordValidationPolicyRepository; +import org.mifosplatform.useradministration.domain.PermissionRepository; +import org.mifosplatform.useradministration.exception.PasswordValidationPolicyNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Caching; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Service +public class PasswordValidationPolicyWritePlatformServiceJpaRepositoryImpl implements PasswordValidationPolicyWritePlatformService { + + private final static Logger logger = LoggerFactory.getLogger(PasswordValidationPolicyWritePlatformServiceJpaRepositoryImpl.class); + private final PlatformSecurityContext context; + private final PasswordValidationPolicyRepository validationRepository; + + @Autowired + public PasswordValidationPolicyWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final PasswordValidationPolicyRepository validationPolicyRepository, + final PermissionRepository permissionRepository) { + this.context = context; + this.validationRepository = validationPolicyRepository; + + } + + + /* + * Guaranteed to throw an exception no matter what the data integrity issue + * is. + */ + private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) { + + logAsErrorUnexpectedDataIntegrityException(dve); + throw new PlatformDataIntegrityException("error.msg.role.unknown.data.integrity.issue", + "Unknown data integrity issue with resource."); + } + + private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) { + logger.error(dve.getMessage(), dve); + } + + @Transactional + @Override + public CommandProcessingResult activate(final Long validationPolicyId, final JsonCommand command) { + try { + this.context.authenticatedUser(); + + final List validationPolicies = this.validationRepository.findAll(); + + Map changes = new LinkedHashMap<>(1); + + boolean found = false; + + for(PasswordValidationPolicy policy:validationPolicies){ + + if(policy.getId().equals(validationPolicyId)){ + + found = true; + + if(!policy.isActive()){ + changes = policy.active(); + } + } + else if(policy.isActive() && !policy.getId().equals(validationPolicyId)){ + policy.deActive(); + + } + } + + if(!found) { throw new PasswordValidationPolicyNotFoundException(validationPolicyId); } + + if (!changes.isEmpty()) { + this.validationRepository.save(validationPolicies); + this.validationRepository.flush(); + } + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(validationPolicyId) // + .with(changes) // + .build(); + } catch (final DataIntegrityViolationException dve) { + handleDataIntegrityIssues(command, dve); + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .build(); + } + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/UserDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/UserDataValidator.java index 05a5ab79f..177564c32 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/UserDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/useradministration/service/UserDataValidator.java @@ -19,6 +19,8 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.useradministration.domain.PasswordValidationPolicy; +import org.mifosplatform.useradministration.domain.PasswordValidationPolicyRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -36,9 +38,12 @@ public final class UserDataValidator { private final FromJsonHelper fromApiJsonHelper; + private final PasswordValidationPolicyRepository passwordValidationPolicy; + @Autowired - public UserDataValidator(final FromJsonHelper fromApiJsonHelper) { + public UserDataValidator(final FromJsonHelper fromApiJsonHelper, final PasswordValidationPolicyRepository passwordValidationPolicy) { this.fromApiJsonHelper = fromApiJsonHelper; + this.passwordValidationPolicy = passwordValidationPolicy; } public void validateForCreate(final String json) { @@ -69,7 +74,11 @@ public final class UserDataValidator { } else { final String password = this.fromApiJsonHelper.extractStringNamed("password", element); final String repeatPassword = this.fromApiJsonHelper.extractStringNamed("repeatPassword", element); - baseDataValidator.reset().parameter("password").value(password).notBlank().notExceedingLengthOf(50); + final PasswordValidationPolicy validationPolicy = this.passwordValidationPolicy.findActivePasswordValidationPolicy(); + final String regex = validationPolicy.getRegex(); + final String description = validationPolicy.getDescription(); + baseDataValidator.reset().parameter("password").value(password).matchesRegularExpression(regex,description); + if (StringUtils.isNotBlank(password)) { baseDataValidator.reset().parameter("password").value(password).equalToParameter("repeatPassword", repeatPassword); } @@ -145,7 +154,12 @@ public final class UserDataValidator { if (this.fromApiJsonHelper.parameterExists("password", element)) { final String password = this.fromApiJsonHelper.extractStringNamed("password", element); final String repeatPassword = this.fromApiJsonHelper.extractStringNamed("repeatPassword", element); - baseDataValidator.reset().parameter("password").value(password).notBlank().notExceedingLengthOf(50); + + final PasswordValidationPolicy validationPolicy = this.passwordValidationPolicy.findActivePasswordValidationPolicy(); + final String regex = validationPolicy.getRegex(); + final String description = validationPolicy.getDescription(); + baseDataValidator.reset().parameter("password").value(password).matchesRegularExpression(regex,description); + if (StringUtils.isNotBlank(password)) { baseDataValidator.reset().parameter("password").value(password).equalToParameter("repeatPassword", repeatPassword); } diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V206_7__password_validation_policy.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V206_7__password_validation_policy.sql new file mode 100644 index 000000000..8a2add20b --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V206_7__password_validation_policy.sql @@ -0,0 +1,31 @@ + +CREATE TABLE IF NOT EXISTS `m_password_validation_policy` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `regex` text NOT NULL, + `description` text NOT NULL, + `active` tinyint(4) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; + + +INSERT INTO `m_password_validation_policy` ( +`id` , +`regex` , +`description` , +`active` +) +VALUES ( +NULL , '^.{1,50}$', 'Password most be at least 1 character and not more that 50 characters long', '1' +); + +INSERT INTO `m_password_validation_policy` ( +`id` , +`regex` , +`description` , +`active` +) +VALUES ( +NULL , '^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\\s).{6,50}$', 'Password must be at least 6 characters, no more than 50 characters long, must include at least one upper case letter, one lower case letter, one numeric digit and no space', '0' +); + + diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V206_8__password_validation_policy_permissions.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V206_8__password_validation_policy_permissions.sql new file mode 100644 index 000000000..2a7575c3e --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V206_8__password_validation_policy_permissions.sql @@ -0,0 +1,7 @@ +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUE ("authorisation","READ_PASSWORD_VALIDATION_POLICY","PASSWORD_VALIDATION_POLICY","READ",0); + +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUE ("authorisation","UPDATE_PASSWORD_VALIDATION_POLICY","PASSWORD_VALIDATION_POLICY","UPDATE",0); + +