diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index 1720237ff..2cb8fde3d 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -2032,6 +2032,14 @@
Enable/Disable Permissions for Maker Checker |
|
+
+ | Password Validation Policy |
+ paswordValidationPolicy |
+ |
+ List validation Policy |
+ Activate policy |
+ |
+
@@ -30853,7 +30861,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 351ab7357..c0297b667 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
@@ -1022,4 +1022,8 @@ public class CommandWrapper {
return this.entityName.equalsIgnoreCase("WORKINGDAYS");
}
+ public boolean isPasswordValidationPolicyResource() {
+ return this.entityName.equalsIgnoreCase("PASSWORD_VALIDATION_POLICY");
+ }
+
}
\ 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 ae8ef1034..e6e46d72d 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
@@ -2361,4 +2361,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 a094c43b0..d90498f68 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
@@ -923,10 +923,17 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
- } else if(wrapper.isWorkingDaysResource()) {
- if(wrapper.isUpdate()){
- handler = this.applicationContext.getBean("updateWorkingDaysCommandHandler",NewCommandSourceHandler.class);
- }else {
+ } else if (wrapper.isWorkingDaysResource()) {
+ if (wrapper.isUpdate()) {
+ handler = this.applicationContext.getBean("updateWorkingDaysCommandHandler", NewCommandSourceHandler.class);
+ } else {
+ throw new UnsupportedCommandException(wrapper.commandName());
+ }
+ } else if (wrapper.isPasswordValidationPolicyResource()) {
+ if (wrapper.isUpdate()) {
+ handler = this.applicationContext.getBean("updatePasswordValidationPolicyCommandHandler", NewCommandSourceHandler.class);
+
+ } else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else {
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 c7de11a61..bd3b633db 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);
}
@@ -151,7 +160,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);
+
+