You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by na...@apache.org on 2017/11/27 12:11:25 UTC
[2/4] fineract git commit: Two-Factor Authentication
Two-Factor Authentication
Technical Spec: https://mifosforge.jira.com/wiki/spaces/projects/pages/185277689/GSoC+2017+-+Two-Factor+Authentication
Project: http://git-wip-us.apache.org/repos/asf/fineract/repo
Commit: http://git-wip-us.apache.org/repos/asf/fineract/commit/1a966e8e
Tree: http://git-wip-us.apache.org/repos/asf/fineract/tree/1a966e8e
Diff: http://git-wip-us.apache.org/repos/asf/fineract/diff/1a966e8e
Branch: refs/heads/develop
Commit: 1a966e8e735dce586feae1e1c1372902a03f31e4
Parents: b56cc43
Author: Alex Ivanov <al...@gmail.com>
Authored: Mon Jun 12 20:20:26 2017 +0100
Committer: Alex Ivanov <al...@gmail.com>
Committed: Fri Sep 22 14:24:52 2017 +0100
----------------------------------------------------------------------
api-docs/apiLive.htm | 242 +++++++++++++++
api-docs/apidocs.css | 4 +
fineract-provider/build.gradle | 36 ++-
.../basicauth/twofactor/application.properties | 21 ++
.../oauth/twofactor/application.properties | 21 ++
.../commands/service/CommandWrapperBuilder.java | 16 +-
.../domain/ConfigurationDomainService.java | 9 +-
.../domain/ConfigurationDomainServiceJpa.java | 37 +++
.../boot/AbstractApplicationConfiguration.java | 3 +-
.../core/boot/WebTwoFactorXmlConfiguration.java | 36 +++
.../security/api/AuthenticationApiResource.java | 14 +-
.../security/api/TwoFactorApiResource.java | 132 ++++++++
.../api/TwoFactorConfigurationApiResource.java | 86 ++++++
.../security/api/UserDetailsApiResource.java | 17 +-
.../InvalidateTFAccessTokenCommandHandler.java | 108 +++++++
.../UpdateTwoFactorConfigCommandHandler.java | 60 ++++
.../TwoFactorConfigurationConstants.java | 59 ++++
.../security/constants/TwoFactorConstants.java | 30 ++
.../security/data/AccessTokenData.java | 47 +++
.../data/AuthenticatedOauthUserData.java | 12 +-
.../security/data/AuthenticatedUserData.java | 12 +-
.../security/data/OTPDeliveryMethod.java | 38 +++
.../security/data/OTPMetadata.java | 53 ++++
.../security/data/OTPRequest.java | 53 ++++
.../data/TwoFactorConfigurationValidator.java | 120 ++++++++
.../security/domain/OTPRequestRepository.java | 51 ++++
.../security/domain/TFAccessToken.java | 137 +++++++++
.../domain/TFAccessTokenRepository.java | 31 ++
.../security/domain/TwoFactorConfiguration.java | 84 +++++
.../TwoFactorConfigurationRepository.java | 34 +++
.../exception/AccessTokenInvalidIException.java | 28 ++
.../OTPDeliveryMethodInvalidException.java | 29 ++
.../exception/OTPTokenInvalidException.java | 28 ++
.../InsecureTwoFactorAuthenticationFilter.java | 82 +++++
.../filter/TwoFactorAuthenticationFilter.java | 139 +++++++++
.../service/AccessTokenGenerationService.java | 24 ++
.../security/service/RandomOTPGenerator.java | 38 +++
.../service/TwoFactorConfigurationService.java | 51 ++++
.../TwoFactorConfigurationServiceImpl.java | 304 +++++++++++++++++++
.../security/service/TwoFactorService.java | 43 +++
.../security/service/TwoFactorServiceImpl.java | 229 ++++++++++++++
.../security/service/TwoFactorUtils.java | 47 +++
.../UUIDAccessTokenGenerationService.java | 32 ++
.../useradministration/domain/AppUser.java | 17 ++
.../main/resources/META-INF/spring/ehcache.xml | 4 +
.../META-INF/spring/securityContext.xml | 38 ++-
.../core_db/V336__two_factor_authentication.sql | 63 ++++
47 files changed, 2763 insertions(+), 36 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/api-docs/apiLive.htm
----------------------------------------------------------------------
diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index bc0c43e..9a87171 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -2629,6 +2629,30 @@
<td></td>
</tr>
<tr>
+ <td><a href="#twofactor">Two-Factor Authentication</a></td>
+ <td>twofactor</td>
+ <td><a href="#twofactor_request">Request OTP</a></td>
+ <td><a href="#twofactor_deliverymethods">List OTP delivery methods</a></td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>twofactor/validate</td>
+ <td><a href="#twofactor_validate">Validate OTP</a></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>twofactor/invalidate</td>
+ <td><a href="#twofactor_invalidate">Invalidate Access Token</a></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
<td><a href="#users">User</a></td>
<td>users</td>
<td><a href="#users_create">Create a User</a></td>
@@ -2942,6 +2966,16 @@
external service Configuration</a></td>
<td></td>
</tr>
+ <tr>
+ <td><a href="#twofactor_config_get">Two-Factor Configuration</a></td>
+ <td>twofactor/configure</td>
+ <td></td>
+ <td><a href="#twofactor_config_get">Retrieve
+ Two-Factor Configuration</a></td>
+ <td><a href="#twofactor_config_update">Update
+ Two-Factor Configuration</a></td>
+ <td></td>
+ </tr>
</table>
</div>
</div>
@@ -3931,6 +3965,11 @@
the platform setup <a href="https://github.com/openMF/mifosx/wiki/Launching-platform-server-locally-from-the-command-line#choosing-authentication-mechanism"> wiki</a> for additional details.
</p>
<p>
+ Optionally, two-factor authentication can be enabled by using
+ <i>-Ptwofactor=enabled</i> on gradle build.
+ Details of the authentication workflow with two-factor authentication enabled can be found <a href="#twofactor">here</a>.
+ </p>
+ <p>
The platform has been configured to reject plain HTTP requests and
to expect all API requests to be made over <a
href="http://en.wikipedia.org/wiki/HTTP_Secure">HTTPS</a>. All
@@ -4057,6 +4096,142 @@ function executeAjaxRequest(url, verbType, jsonData, authKey, successFunction, e
</code>
</div>
</div>
+ <a id="twofactor" name="twofactor" class="old-syle-anchor"> </a>
+ <div class="method-section">
+ <div class="method-description">
+ <h3>Two-Factor Authentication</h3>
+ <p>
+ Two-Factor authentication is supported by requesting & verifying
+ one-time passwords(OTP). OTPs are sent via SMS & email.
+ </p>
+ <p>
+ By default, two-factor authentication is disabled by default.
+ More information on how to enable TFA can be found <a href="#authentication_overview">here</a>.
+ </p>
+ <p>
+ Two-factor authentication workflow:
+ <ol class="normalli">
+ <li class="normalli">User authticates via BasicAuth / oAauth</li>
+ <li>Client requests a list of supported OTP delivery methods for the authenticated user(<a href="#twofactor_deliverymethods">Get Delivery Methods</a>)</li>
+ <li>User selects an OTP delivery method and client sends a request for OTP(<a href="#twofactor_request">Request OTP</a>)</li>
+ <li>User receives an OTP and the client sends it for verification(<a href="#twofactor_validate">Validate OTP</a>)</li>
+ <li>If the OTP is valid, an access token is returned</li>
+ <li>The access token is sent in following requestes to the server as a header <i>Fineract-Platform-TFA-Token</i></li>
+ <li>On session end, the access token should be invalidated<a href="#twofactor_invalidate">Invalidate Access Token</a>)</li>
+ </ol>
+ </p>
+ <p>
+ Two-Factor authentication and delivery methods can be configured via
+ the <a href="#twofactor_configure"<i>/twofactor/configure</i> endpoint.</a>
+ </p>
+ </div>
+ <div class="method-example">
+
+ </div>
+ </div>
+
+ <a id="twofactor_deliverymethods" name="twofactor_deliverymethods" class="old-syle-anchor"> </a>
+ <div class="method-section">
+ <div class="method-description">
+ <h4>Get Delivery Methods</h4>
+ <p>Returns a list of possible OTP delivery methods for the current user</p>
+ <p>Requires first-factor authenticated user.</p>
+ </div>
+ <div class="method-example">
+ <code class="method-declaration">GET https://DomainName/api/v1/twofactor</code>
+ <code class="method-response">
+[
+ {
+ "name": "sms",
+ "target": "08888888888"
+ },
+ {
+ "name": "email",
+ "target": "user@example.com"
+ }
+]</code>
+ </div>
+ </div>
+
+ <a id="twofactor_request" name="twofactor_request" class="old-syle-anchor"> </a>
+ <div class="method-section">
+ <div class="method-description">
+ <h4>Request OTP</h4>
+ <p>Requests an OTP.</p>
+ <p>Requires first-factor authenticated user.</p>
+ <h5>Arguments</h5>
+ <dl class="argument-list">
+ <dt>deliveryMethod</dt>
+ <dd>
+ String<span> mandatory, the delivery method name</span>
+ </dd>
+ <dt>extendedToken</dt>
+ <dd>
+ boolean<span> optional, whether to request an extended token, default false</span>
+ </dd>
+ </dl>
+ </div>
+ <div class="method-example">
+ <code class="method-declaration">POST https://DomainName/api/v1/twofactor?deliveryMethod=sms&extendedToken=false</code>
+ <code class="method-response">
+{
+ "requestTime": 1500000000000,
+ "tokenLiveTimeInSec": 300,
+ "extendedAccessToken": false,
+ "deliveryMethod": {
+ "name": "sms",
+ "target": "08888888888"
+ }
+}</code>
+ </div>
+ </div>
+
+ <a id="twofactor_validate" name="twofactor_validate" class="old-syle-anchor"> </a>
+ <div class="method-section">
+ <div class="method-description">
+ <h4>Validate OTP</h4>
+ <p>Validates an OTP. If the OTP is valid, an access token is created.</p>
+ <p>The returned access token is later sent as a header <i>Fineract-Platform-TFA-Token</i>.</p>
+ <p>Requires first-factor authenticated user.</p>
+ <h5>Arguments</h5>
+ <dl class="argument-list">
+ <dt>token</dt>
+ <dd>
+ String<span> mandatory, the OTP to validate</span>
+ </dd>
+ </dl>
+ </div>
+ <div class="method-example">
+ <code class="method-declaration">POST https://DomainName/api/v1/twofactor/validate?token=YYYYY</code>
+ <code class="method-response">
+{
+ "token": "cb0bb6e33fc540709d50a16eb2e555f9",
+ "validFrom": 1501530702801,
+ "validTo": 1501617102801
+}</code>
+ </div>
+ </div>
+
+ <a id="twofactor_invalidate" name="twofactor_invalidate" class="old-syle-anchor"> </a>
+ <div class="method-section">
+ <div class="method-description">
+ <h4>Invalidate Access Token</h4>
+ <p>Invalidates an access token.</p>
+ <p>Two factor access tokens should be invalidated on logout.</p>
+ <p>Requires fully authenticated user.</p>
+ </div>
+ <div class="method-example">
+ <code class="method-declaration">POST https://DomainName/api/v1/twofactor/invalidate</code>
+ <code class="method-request">
+{
+ "token": "cb0bb6e33fc540709d50a16eb2e555f9"
+}</code>
+ <code class="method-response">
+{
+ "resourceIdentifier": "cb0bb6e33fc540709d50a16eb2e555f9"
+}</code>
+ </div>
+ </div>
<a id="batch_api" name="batch_api" class="old-syle-anchor"> </a>
<div class="method-section">
@@ -19391,6 +19566,73 @@ Content-Type: application/json
</div>
</div>
+ <a id="twofactor_config" name="twofactor_config"
+ class="old-syle-anchor"> </a>
+ <div class="method-section">
+ <div class="method-description">
+ <h3>Two-Factor Configuration</h3>
+ <p>The following section describes the way to configure two-factor authentication</p>
+ <p>Two-Factor Authentication has to be enabled by either building with Gradle arguments
+ <i>-Ptwofactor=enabled</i> or enabling the <i>twofactor</i> profile via env. variable
+ </p>
+ <p>In order for SMS to be enabled an SMS bridge has to be setup with the message-gateway service.</p>
+ </div>
+ <div class="method-example">
+ </div>
+ </div>
+
+ <a id="twofactor_config_get" name="twofactor_config_get" class="old-syle-anchor"> </a>
+ <div class="method-section">
+ <div class="method-description">
+ <h4>Retrieve Two-Factor Configuration</h4>
+ <p>Returns available two-factor configuration.</p>
+ </div>
+ <div class="method-example">
+ <code class="method-declaration"> GET https://DomainName/api/v1/twofactor/configure
+ </code>
+ <code class="method-response">
+{
+ "otp-delivery-email-body": "Hello {{username}}.\n\nYour OTP login token is {{token}}.",
+ "otp-delivery-sms-enable": true,
+ "otp-delivery-sms-provider": 6,
+ "otp-delivery-email-subject": "Fineract Two-Factor Authentication Token",
+ "otp-token-length": 5,
+ "access-token-live-time-extended": 604800,
+ "otp-delivery-email-enable": true,
+ "otp-token-live-time": 300,
+ "otp-delivery-sms-text": "Your authentication token for Fineract is {{token}}.",
+ "access-token-live-time": 86400
+}
+ </code>
+ </div>
+ </div>
+
+ <a id="twofactor_config_update" name="twofactor_config_update" class="old-syle-anchor"> </a>
+ <div class="method-section">
+ <div class="method-description">
+ <h4>Update Two-Factor Configuration</h4>
+ <p>Update two-factor configuration.</p>
+ </div>
+ <div class="method-example">
+ <code class="method-declaration"> PUT https://DomainName/api/v1/twofactor/configure
+ </code>
+ <code class="method-request">
+{
+ "otp-delivery-sms-provider": 7
+ "otp-delivery-sms-enable": false
+}
+ </code>
+ <code class="method-response">
+{
+ "changes": {
+ "otp-delivery-sms-enable": false,
+ "otp-delivery-sms-provider": 7
+ }
+}
+ </code>
+ </div>
+ </div>
+
<!-- Fund starts here -->
<a id="funds" name="funds" class="old-syle-anchor"> </a>
<div class="method-section">
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/api-docs/apidocs.css
----------------------------------------------------------------------
diff --git a/api-docs/apidocs.css b/api-docs/apidocs.css
index f8816a7..21bc11c 100644
--- a/api-docs/apidocs.css
+++ b/api-docs/apidocs.css
@@ -745,4 +745,8 @@ ul.field li {
tt {
font-size: 9.5pt;
+}
+
+ol.normalli li {
+ list-style-type: decimal;
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/build.gradle
----------------------------------------------------------------------
diff --git a/fineract-provider/build.gradle b/fineract-provider/build.gradle
index 771fcde..5a2b8b3 100644
--- a/fineract-provider/build.gradle
+++ b/fineract-provider/build.gradle
@@ -193,17 +193,33 @@ if (project.hasProperty('env') && project.getProperty('env') == 'dev') {
/* Enable Oauth2 authentication based on environment, default to HTTP basic auth */
if (project.hasProperty('security') && project.getProperty('security') == 'oauth') {
- copy {
- from './properties/oauth/'
- into 'src/main/resources/'
- include '*.properties'
- }
+ if(project.hasProperty('twofactor') && project.getProperty('twofactor') == 'enabled') {
+ copy {
+ from './properties/oauth/twofactor/'
+ into 'src/main/resources/'
+ include '*.properties'
+ }
+ } else {
+ copy {
+ from './properties/oauth/'
+ into 'src/main/resources/'
+ include '*.properties'
+ }
+ }
} else {
- copy {
- from './properties/basicauth/'
- into 'src/main/resources/'
- include '*.properties'
- }
+ if(project.hasProperty('twofactor') && project.getProperty('twofactor') == 'enabled') {
+ copy {
+ from './properties/basicauth/twofactor/'
+ into 'src/main/resources/'
+ include '*.properties'
+ }
+ } else {
+ copy {
+ from './properties/basicauth/'
+ into 'src/main/resources/'
+ include '*.properties'
+ }
+ }
}
task dist(type:Zip){
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/properties/basicauth/twofactor/application.properties
----------------------------------------------------------------------
diff --git a/fineract-provider/properties/basicauth/twofactor/application.properties b/fineract-provider/properties/basicauth/twofactor/application.properties
new file mode 100644
index 0000000..5b4d496
--- /dev/null
+++ b/fineract-provider/properties/basicauth/twofactor/application.properties
@@ -0,0 +1,21 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+spring.profiles.default=basicauth
+spring.profiles.active=basicauth,twofactor
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/properties/oauth/twofactor/application.properties
----------------------------------------------------------------------
diff --git a/fineract-provider/properties/oauth/twofactor/application.properties b/fineract-provider/properties/oauth/twofactor/application.properties
new file mode 100644
index 0000000..12cbfbe
--- /dev/null
+++ b/fineract-provider/properties/oauth/twofactor/application.properties
@@ -0,0 +1,21 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+spring.profiles.default=basicauth
+spring.profiles.active=oauth,twofactor
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
index 0ad3612..93bd160 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
@@ -2929,7 +2929,7 @@ public class CommandWrapperBuilder {
this.href = "/smscampaigns/"+resourceId;
return this;
}
-
+
public CommandWrapperBuilder holdAmount(final Long accountId) {
this.actionName = "HOLDAMOUNT";
this.entityName = "SAVINGSACCOUNT";
@@ -3041,4 +3041,18 @@ public class CommandWrapperBuilder {
this.json = "{}";
return this;
}
+
+ public CommandWrapperBuilder invalidateTwoFactorAccessToken() {
+ this.actionName = "INVALIDATE";
+ this.entityName = "TWOFACTOR_ACCESSTOKEN";
+ this.href = "/twofactor/invalidate";
+ return this;
+ }
+
+ public CommandWrapperBuilder updateTwoFactorConfiguration() {
+ this.actionName = "UPDATE";
+ this.entityName = "TWOFACTOR_CONFIGURATION";
+ this.href = "/twofactor/configure";
+ return this;
+ }
}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
index 4abbb4b..f2eab64 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
@@ -85,5 +85,12 @@ public interface ConfigurationDomainService {
Long getDailyTPTLimit();
void removeGlobalConfigurationPropertyDataFromCache(String propertyName);
-
+
+ boolean isSMSOTPDeliveryEnabled();
+
+ boolean isEmailOTPDeliveryEnabled();
+
+ Integer retrieveOTPCharacterLength();
+
+ Integer retrieveOTPLiveTime();
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
index 95170f8..3d51ddd 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
@@ -287,6 +287,43 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
configurations.remove(key);
}
+ @Override
+ public boolean isSMSOTPDeliveryEnabled() {
+ final String propertyName = "use-sms-for-2fa";
+ final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(propertyName);
+ return property.isEnabled();
+ }
+
+ @Override
+ public boolean isEmailOTPDeliveryEnabled() {
+ final String propertyName = "use-email-for-2fa";
+ final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(propertyName);
+ return property.isEnabled();
+ }
+
+ @Override
+ public Integer retrieveOTPCharacterLength() {
+ final String propertyName = "otp-character-length";
+ final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(propertyName);
+ int defaultValue = 6;
+ int value = property.getValue().intValue();
+ if(value < 1)
+ return defaultValue;
+ return value;
+ }
+
+ @Override
+ public Integer retrieveOTPLiveTime() {
+ final String propertyName = "otp-validity-period";
+ final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(propertyName);
+ int defaultValue = 300;
+ int value = property.getValue().intValue();
+ if(value < 1) {
+ return defaultValue;
+ }
+ return value;
+ }
+
private GlobalConfigurationPropertyData getGlobalConfigurationPropertyData(final String propertyName) {
String identifier = ThreadLocalContextUtil.getTenant().getTenantIdentifier();
String key = identifier + "_" + propertyName;
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/AbstractApplicationConfiguration.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/AbstractApplicationConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/AbstractApplicationConfiguration.java
index 7823a0d..dd8fc20 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/AbstractApplicationConfiguration.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/AbstractApplicationConfiguration.java
@@ -40,7 +40,8 @@ import org.springframework.context.annotation.PropertySource;
* and MariaDB4j (because those differ in the subclasses).
*/
@Configuration
-@Import({ WebXmlConfiguration.class, WebXmlOauthConfiguration.class, WebFrontEndConfiguration.class })
+@Import({ WebXmlConfiguration.class, WebXmlOauthConfiguration.class, WebFrontEndConfiguration.class,
+ WebTwoFactorXmlConfiguration.class })
@ImportResource({ "classpath*:META-INF/spring/appContext.xml" })
@PropertySource(value="classpath:META-INF/spring/jdbc.properties")
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class,
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/WebTwoFactorXmlConfiguration.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/WebTwoFactorXmlConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/WebTwoFactorXmlConfiguration.java
new file mode 100644
index 0000000..e8f1688
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/WebTwoFactorXmlConfiguration.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.core.boot;
+
+import org.apache.fineract.infrastructure.security.filter.TwoFactorAuthenticationFilter;
+import org.springframework.boot.context.embedded.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+
+@Configuration
+public class WebTwoFactorXmlConfiguration {
+
+ @Bean
+ public FilterRegistrationBean twoFactorFilterBean(TwoFactorAuthenticationFilter filter) {
+ FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
+ registrationBean.setEnabled(false);
+ return registrationBean;
+ }
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/AuthenticationApiResource.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/AuthenticationApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/AuthenticationApiResource.java
index 1638359..e5c20ed 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/AuthenticationApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/AuthenticationApiResource.java
@@ -30,8 +30,10 @@ import javax.ws.rs.core.MediaType;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.constants.TwoFactorConstants;
import org.apache.fineract.infrastructure.security.data.AuthenticatedUserData;
import org.apache.fineract.infrastructure.security.service.SpringSecurityPlatformSecurityContext;
+import org.apache.fineract.infrastructure.security.service.TwoFactorUtils;
import org.apache.fineract.useradministration.data.RoleData;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.Role;
@@ -56,15 +58,17 @@ public class AuthenticationApiResource {
private final DaoAuthenticationProvider customAuthenticationProvider;
private final ToApiJsonSerializer<AuthenticatedUserData> apiJsonSerializerService;
private final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext;
+ private final TwoFactorUtils twoFactorUtils;
@Autowired
public AuthenticationApiResource(
@Qualifier("customAuthenticationProvider") final DaoAuthenticationProvider customAuthenticationProvider,
final ToApiJsonSerializer<AuthenticatedUserData> apiJsonSerializerService,
- final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext) {
+ final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext, TwoFactorUtils twoFactorUtils) {
this.customAuthenticationProvider = customAuthenticationProvider;
this.apiJsonSerializerService = apiJsonSerializerService;
this.springSecurityPlatformSecurityContext = springSecurityPlatformSecurityContext;
+ this.twoFactorUtils = twoFactorUtils;
}
@POST
@@ -100,12 +104,16 @@ public class AuthenticationApiResource {
final EnumOptionData organisationalRole = principal.organisationalRoleData();
+ boolean isTwoFactorRequired = twoFactorUtils.isTwoFactorAuthEnabled() && !
+ principal.hasSpecificPermissionTo(TwoFactorConstants.BYPASS_TWO_FACTOR_PERMISSION);
if (this.springSecurityPlatformSecurityContext.doesPasswordHasToBeRenewed(principal)) {
- authenticatedUserData = new AuthenticatedUserData(username, principal.getId(), new String(base64EncodedAuthenticationKey));
+ authenticatedUserData = new AuthenticatedUserData(username, principal.getId(),
+ new String(base64EncodedAuthenticationKey), isTwoFactorRequired);
} else {
authenticatedUserData = new AuthenticatedUserData(username, officeId, officeName, staffId, staffDisplayName,
- organisationalRole, roles, permissions, principal.getId(), new String(base64EncodedAuthenticationKey));
+ organisationalRole, roles, permissions, principal.getId(),
+ new String(base64EncodedAuthenticationKey), isTwoFactorRequired);
}
}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/TwoFactorApiResource.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/TwoFactorApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/TwoFactorApiResource.java
new file mode 100644
index 0000000..482906a
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/TwoFactorApiResource.java
@@ -0,0 +1,132 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.api;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.data.AccessTokenData;
+import org.apache.fineract.infrastructure.security.data.OTPDeliveryMethod;
+import org.apache.fineract.infrastructure.security.data.OTPMetadata;
+import org.apache.fineract.infrastructure.security.data.OTPRequest;
+import org.apache.fineract.infrastructure.security.domain.TFAccessToken;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.infrastructure.security.service.TwoFactorService;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+@Path("/twofactor")
+@Component
+@Profile("twofactor")
+@Scope("singleton")
+public class TwoFactorApiResource {
+
+
+ private final ToApiJsonSerializer<OTPMetadata> otpRequestSerializer;
+ private final ToApiJsonSerializer<OTPDeliveryMethod> otpDeliveryMethodSerializer;
+ private final ToApiJsonSerializer<AccessTokenData> accessTokenSerializer;
+ private final DefaultToApiJsonSerializer<Map<String, Object>> toApiJsonSerializer;
+
+ private final PlatformSecurityContext context;
+ private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
+ private final TwoFactorService twoFactorService;
+
+
+
+ @Autowired
+ public TwoFactorApiResource(ToApiJsonSerializer<OTPMetadata> otpRequestSerializer,
+ ToApiJsonSerializer<OTPDeliveryMethod> otpDeliveryMethodSerializer,
+ ToApiJsonSerializer<AccessTokenData> accessTokenSerializer,
+ DefaultToApiJsonSerializer<Map<String, Object>> toApiJsonSerializer,
+ PlatformSecurityContext context,
+ PortfolioCommandSourceWritePlatformService
+ commandsSourceWritePlatformService,
+ TwoFactorService twoFactorService) {
+ this.otpRequestSerializer = otpRequestSerializer;
+ this.otpDeliveryMethodSerializer = otpDeliveryMethodSerializer;
+ this.accessTokenSerializer = accessTokenSerializer;
+ this.toApiJsonSerializer = toApiJsonSerializer;
+ this.context = context;
+ this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
+ this.twoFactorService = twoFactorService;
+ }
+
+
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String getOTPDeliveryMethods(@Context final UriInfo uriInfo) {
+ AppUser user = context.authenticatedUser();
+
+ List<OTPDeliveryMethod> otpDeliveryMethods = twoFactorService.getDeliveryMethodsForUser(user);
+ return this.otpDeliveryMethodSerializer.serialize(otpDeliveryMethods);
+ }
+
+ @POST
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String requestToken(@QueryParam("deliveryMethod") final String deliveryMethod,
+ @QueryParam("extendedToken") @DefaultValue("false") boolean extendedAccessToken,
+ @Context final UriInfo uriInfo) {
+ final AppUser user = context.authenticatedUser();
+
+ final OTPRequest request = twoFactorService.createNewOTPToken(user, deliveryMethod, extendedAccessToken);
+ return this.otpRequestSerializer.serialize(request.getMetadata());
+ }
+
+ @Path("validate")
+ @POST
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String validate(@QueryParam("token") final String token) {
+ final AppUser user = context.authenticatedUser();
+
+ TFAccessToken accessToken = twoFactorService.createAccessTokenFromOTP(user, token);
+
+ return accessTokenSerializer.serialize(accessToken.toTokenData());
+ }
+
+ @Path("invalidate")
+ @POST
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String updateConfiguration(final String apiRequestBodyAsJson) {
+ final CommandWrapper commandRequest = new CommandWrapperBuilder()
+ .invalidateTwoFactorAccessToken().withJson(apiRequestBodyAsJson).build();
+ final CommandProcessingResult result = this.commandsSourceWritePlatformService.
+ logCommandSource(commandRequest);
+
+ return this.toApiJsonSerializer.serialize(result);
+ }
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/TwoFactorConfigurationApiResource.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/TwoFactorConfigurationApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/TwoFactorConfigurationApiResource.java
new file mode 100644
index 0000000..13fd69e
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/TwoFactorConfigurationApiResource.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.api;
+
+
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+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.MediaType;
+
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.infrastructure.security.service.TwoFactorConfigurationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+@Path("/twofactor/configure")
+@Consumes({ MediaType.APPLICATION_JSON })
+@Produces({ MediaType.APPLICATION_JSON })
+@Component
+@Scope("singleton")
+@Profile("twofactor")
+public class TwoFactorConfigurationApiResource {
+
+ private final String resourceNameForPermissions = "TWOFACTOR_CONFIG";
+
+ private final PlatformSecurityContext context;
+ private final TwoFactorConfigurationService configurationService;
+ private final DefaultToApiJsonSerializer<Map<String, Object>> toApiJsonSerializer;
+ private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
+
+ @Autowired
+ public TwoFactorConfigurationApiResource(PlatformSecurityContext context,
+ TwoFactorConfigurationService configurationService,
+ DefaultToApiJsonSerializer<Map<String, Object>> toApiJsonSerializer,
+ PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
+ this.context = context;
+ this.configurationService = configurationService;
+ this.toApiJsonSerializer = toApiJsonSerializer;
+ this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
+ }
+
+
+ @GET
+ public String retrieveAll() {
+ this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
+ Map<String, Object> configurationMap = configurationService.retrieveAll();
+ return toApiJsonSerializer.serialize(configurationMap);
+ }
+
+ @PUT
+ public String updateConfiguration(final String apiRequestBodyAsJson) {
+ final CommandWrapper commandRequest = new CommandWrapperBuilder()
+ .updateTwoFactorConfiguration().withJson(apiRequestBodyAsJson).build();
+ final CommandProcessingResult result = this.commandsSourceWritePlatformService.
+ logCommandSource(commandRequest);
+
+ return this.toApiJsonSerializer.serialize(result);
+ }
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/UserDetailsApiResource.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/UserDetailsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/UserDetailsApiResource.java
index 67f0616..d05f589 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/UserDetailsApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/api/UserDetailsApiResource.java
@@ -30,8 +30,10 @@ import javax.ws.rs.core.MediaType;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.constants.TwoFactorConstants;
import org.apache.fineract.infrastructure.security.data.AuthenticatedOauthUserData;
import org.apache.fineract.infrastructure.security.service.SpringSecurityPlatformSecurityContext;
+import org.apache.fineract.infrastructure.security.service.TwoFactorUtils;
import org.apache.fineract.useradministration.data.RoleData;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.Role;
@@ -56,14 +58,17 @@ public class UserDetailsApiResource {
private final ResourceServerTokenServices tokenServices;
private final ToApiJsonSerializer<AuthenticatedOauthUserData> apiJsonSerializerService;
private final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext;
+ private final TwoFactorUtils twoFactorUtils;
@Autowired
public UserDetailsApiResource(@Qualifier("tokenServices") final ResourceServerTokenServices tokenServices,
final ToApiJsonSerializer<AuthenticatedOauthUserData> apiJsonSerializerService,
- final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext) {
+ final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext,
+ final TwoFactorUtils twoFactorUtils) {
this.tokenServices = tokenServices;
this.apiJsonSerializerService = apiJsonSerializerService;
this.springSecurityPlatformSecurityContext = springSecurityPlatformSecurityContext;
+ this.twoFactorUtils = twoFactorUtils;
}
@GET
@@ -96,12 +101,16 @@ public class UserDetailsApiResource {
final EnumOptionData organisationalRole = principal.organisationalRoleData();
+ final boolean requireTwoFactorAuth = twoFactorUtils.isTwoFactorAuthEnabled()
+ && !principal.hasSpecificPermissionTo(TwoFactorConstants.BYPASS_TWO_FACTOR_PERMISSION);
if (this.springSecurityPlatformSecurityContext.doesPasswordHasToBeRenewed(principal)) {
- authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(), principal.getId(), accessToken);
+ authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(),
+ principal.getId(), accessToken, requireTwoFactorAuth);
} else {
- authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(), officeId, officeName, staffId, staffDisplayName,
- organisationalRole, roles, permissions, principal.getId(), accessToken);
+ authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(),
+ officeId, officeName, staffId, staffDisplayName, organisationalRole, roles,
+ permissions, principal.getId(), accessToken, requireTwoFactorAuth);
}
return this.apiJsonSerializerService.serialize(authenticatedUserData);
}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/command/InvalidateTFAccessTokenCommandHandler.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/command/InvalidateTFAccessTokenCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/command/InvalidateTFAccessTokenCommandHandler.java
new file mode 100644
index 0000000..1ede412
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/command/InvalidateTFAccessTokenCommandHandler.java
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.command;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
+import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.security.constants.TwoFactorConstants;
+import org.apache.fineract.infrastructure.security.domain.TFAccessToken;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.infrastructure.security.service.TwoFactorService;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+
+
+@Service
+@CommandType(entity = "TWOFACTOR_ACCESSTOKEN", action = "INVALIDATE")
+@Profile("twofactor")
+public class InvalidateTFAccessTokenCommandHandler implements NewCommandSourceHandler {
+
+
+ private final TwoFactorService twoFactorService;
+ private final PlatformSecurityContext securityContext;
+ private final FromJsonHelper fromJsonHelper;
+
+ @Autowired
+ public InvalidateTFAccessTokenCommandHandler(TwoFactorService twoFactorService,
+ PlatformSecurityContext securityContext,
+ FromJsonHelper fromJsonHelper) {
+ this.twoFactorService = twoFactorService;
+ this.securityContext = securityContext;
+ this.fromJsonHelper = fromJsonHelper;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(JsonCommand command) {
+ validateJson(command.json());
+
+ final AppUser user = securityContext.authenticatedUser();
+
+ final TFAccessToken accessToken = twoFactorService.invalidateAccessToken(user, command);
+
+ return new CommandProcessingResultBuilder()
+ .withCommandId(command.commandId())
+ .withResourceIdAsString(accessToken.getToken())
+ .build();
+ }
+
+ private void validateJson(String json) {
+ if (StringUtils.isBlank(json)) {
+ throw new InvalidJsonException();
+ }
+
+ final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
+ this.fromJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
+ new HashSet<>(Collections.singletonList("token")));
+ final JsonElement element = this.fromJsonHelper.parse(json);
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
+ .resource(TwoFactorConstants.ACCESSTOKEN_RESOURCE_NAME);
+
+ final String token = this.fromJsonHelper.extractStringNamed("token", element);
+ baseDataValidator.reset().parameter("token").value(token).notNull().notBlank();
+
+ if(!dataValidationErrors.isEmpty()) {
+ throw new PlatformApiDataValidationException(dataValidationErrors);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/command/UpdateTwoFactorConfigCommandHandler.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/command/UpdateTwoFactorConfigCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/command/UpdateTwoFactorConfigCommandHandler.java
new file mode 100644
index 0000000..b8c4e60
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/command/UpdateTwoFactorConfigCommandHandler.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.command;
+
+import java.util.Map;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.security.data.TwoFactorConfigurationValidator;
+import org.apache.fineract.infrastructure.security.service.TwoFactorConfigurationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@CommandType(entity = "TWOFACTOR_CONFIGURATION", action = "UPDATE")
+@Profile("twofactor")
+public class UpdateTwoFactorConfigCommandHandler implements NewCommandSourceHandler {
+
+ private final TwoFactorConfigurationService configurationService;
+ private final TwoFactorConfigurationValidator dataValidator;
+
+ @Autowired
+ public UpdateTwoFactorConfigCommandHandler(TwoFactorConfigurationService configurationService,
+ TwoFactorConfigurationValidator dataValidator) {
+ this.configurationService = configurationService;
+ this.dataValidator = dataValidator;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(final JsonCommand command) {
+ this.dataValidator.validateForUpdate(command.json());
+ final Map<String, Object> changes = configurationService.update(command);
+ return new CommandProcessingResultBuilder()
+ .withCommandId(command.commandId())
+ .with(changes)
+ .build();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/constants/TwoFactorConfigurationConstants.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/constants/TwoFactorConfigurationConstants.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/constants/TwoFactorConfigurationConstants.java
new file mode 100644
index 0000000..22818de
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/constants/TwoFactorConfigurationConstants.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.constants;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class TwoFactorConfigurationConstants {
+
+ public static final String RESOURCE_NAME = "TWOFACTOR_CONFIGURATION";
+
+ public static final String ENABLE_EMAIL_DELIVERY = "otp-delivery-email-enable";
+ public static final String EMAIL_SUBJECT = "otp-delivery-email-subject";
+ public static final String EMAIL_BODY = "otp-delivery-email-body";
+
+ public static final String ENABLE_SMS_DELIVERY = "otp-delivery-sms-enable";
+ public static final String SMS_PROVIDER_ID = "otp-delivery-sms-provider";
+ public static final String SMS_MESSAGE_TEXT = "otp-delivery-sms-text";
+
+ public static final String OTP_TOKEN_LIVE_TIME = "otp-token-live-time";
+ public static final String OTP_TOKEN_LENGTH = "otp-token-length";
+
+ public static final String ACCESS_TOKEN_LIVE_TIME = "access-token-live-time";
+ public static final String ACCESS_TOKEN_LIVE_TIME_EXTENDED = "access-token-live-time-extended";
+
+ public static final Set<String> REQUEST_DATA_PARAMETERS =
+ new HashSet<>(Arrays.asList(ENABLE_EMAIL_DELIVERY, EMAIL_SUBJECT, EMAIL_BODY,
+ ENABLE_SMS_DELIVERY, SMS_PROVIDER_ID, SMS_MESSAGE_TEXT, OTP_TOKEN_LIVE_TIME,
+ OTP_TOKEN_LENGTH, ACCESS_TOKEN_LIVE_TIME, ACCESS_TOKEN_LIVE_TIME_EXTENDED));
+
+ public static final List<String> STRING_PARAMETERS =
+ Arrays.asList(EMAIL_SUBJECT, EMAIL_BODY, SMS_MESSAGE_TEXT);
+
+ public static final List<String> BOOLEAN_PARAMETERS =
+ Arrays.asList(ENABLE_EMAIL_DELIVERY, ENABLE_SMS_DELIVERY);
+
+ public static final List<String> NUMBER_PARAMETERS =
+ Arrays.asList(SMS_PROVIDER_ID, OTP_TOKEN_LIVE_TIME, OTP_TOKEN_LENGTH,
+ ACCESS_TOKEN_LIVE_TIME, ACCESS_TOKEN_LIVE_TIME_EXTENDED);
+
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/constants/TwoFactorConstants.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/constants/TwoFactorConstants.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/constants/TwoFactorConstants.java
new file mode 100644
index 0000000..7997c39
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/constants/TwoFactorConstants.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.constants;
+
+public class TwoFactorConstants {
+
+ public static final String ACCESSTOKEN_RESOURCE_NAME = "TWOFACTOR_ACCESSTOKEN";
+
+ public static final String SMS_DELIVERY_METHOD_NAME = "sms";
+ public static final String EMAIL_DELIVERY_METHOD_NAME = "email";
+
+ public static final String BYPASS_TWO_FACTOR_PERMISSION = "BYPASS_TWOFACTOR";
+
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AccessTokenData.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AccessTokenData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AccessTokenData.java
new file mode 100644
index 0000000..ebd27f7
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AccessTokenData.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.data;
+
+import org.joda.time.DateTime;
+
+public class AccessTokenData {
+
+ private final String token;
+
+ private final DateTime validFrom;
+ private final DateTime validTo;
+
+ public AccessTokenData(String token, DateTime validFrom, DateTime validTo) {
+ this.token = token;
+ this.validFrom = validFrom;
+ this.validTo = validTo;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public DateTime getValidFrom() {
+ return validFrom;
+ }
+
+ public DateTime getValidTo() {
+ return validTo;
+ }
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AuthenticatedOauthUserData.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AuthenticatedOauthUserData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AuthenticatedOauthUserData.java
index 830fc67..116daa8 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AuthenticatedOauthUserData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AuthenticatedOauthUserData.java
@@ -54,6 +54,9 @@ public class AuthenticatedOauthUserData {
@SuppressWarnings("unused")
private final boolean shouldRenewPassword;
+ @SuppressWarnings("unused")
+ private final boolean isTwoFactorAuthenticationRequired;
+
public AuthenticatedOauthUserData(final String username, final Collection<String> permissions) {
this.username = username;
this.userId = null;
@@ -67,11 +70,13 @@ public class AuthenticatedOauthUserData {
this.roles = null;
this.permissions = permissions;
this.shouldRenewPassword = false;
+ this.isTwoFactorAuthenticationRequired = false;
}
public AuthenticatedOauthUserData(final String username, final Long officeId, final String officeName, final Long staffId,
final String staffDisplayName, final EnumOptionData organisationalRole, final Collection<RoleData> roles,
- final Collection<String> permissions, final Long userId, final String accessToken) {
+ final Collection<String> permissions, final Long userId, final String accessToken,
+ final boolean isTwoFactorAuthenticationRequired) {
this.username = username;
this.officeId = officeId;
this.officeName = officeName;
@@ -84,9 +89,11 @@ public class AuthenticatedOauthUserData {
this.roles = roles;
this.permissions = permissions;
this.shouldRenewPassword = false;
+ this.isTwoFactorAuthenticationRequired = isTwoFactorAuthenticationRequired;
}
- public AuthenticatedOauthUserData(final String username, final Long userId, final String accessToken) {
+ public AuthenticatedOauthUserData(final String username, final Long userId, final String accessToken,
+ final boolean isTwoFactorAuthenticationRequired) {
this.username = username;
this.officeId = null;
this.officeName = null;
@@ -99,5 +106,6 @@ public class AuthenticatedOauthUserData {
this.roles = null;
this.permissions = null;
this.shouldRenewPassword = true;
+ this.isTwoFactorAuthenticationRequired = isTwoFactorAuthenticationRequired;
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AuthenticatedUserData.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AuthenticatedUserData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AuthenticatedUserData.java
index 513f5df..c4713fc 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AuthenticatedUserData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/AuthenticatedUserData.java
@@ -54,6 +54,9 @@ public class AuthenticatedUserData {
@SuppressWarnings("unused")
private final boolean shouldRenewPassword;
+ @SuppressWarnings("unused")
+ private final boolean isTwoFactorAuthenticationRequired;
+
public AuthenticatedUserData(final String username, final Collection<String> permissions) {
this.username = username;
this.userId = null;
@@ -67,11 +70,13 @@ public class AuthenticatedUserData {
this.roles = null;
this.permissions = permissions;
this.shouldRenewPassword = false;
+ this.isTwoFactorAuthenticationRequired = false;
}
public AuthenticatedUserData(final String username, final Long officeId, final String officeName, final Long staffId,
final String staffDisplayName, final EnumOptionData organisationalRole, final Collection<RoleData> roles,
- final Collection<String> permissions, final Long userId, final String base64EncodedAuthenticationKey) {
+ final Collection<String> permissions, final Long userId, final String base64EncodedAuthenticationKey,
+ final boolean isTwoFactorAuthenticationRequired) {
this.username = username;
this.officeId = officeId;
this.officeName = officeName;
@@ -84,9 +89,11 @@ public class AuthenticatedUserData {
this.roles = roles;
this.permissions = permissions;
this.shouldRenewPassword = false;
+ this.isTwoFactorAuthenticationRequired = isTwoFactorAuthenticationRequired;
}
- public AuthenticatedUserData(final String username, final Long userId, final String base64EncodedAuthenticationKey) {
+ public AuthenticatedUserData(final String username, final Long userId, final String base64EncodedAuthenticationKey,
+ final boolean isTwoFactorAuthenticationRequired) {
this.username = username;
this.officeId = null;
this.officeName = null;
@@ -99,5 +106,6 @@ public class AuthenticatedUserData {
this.roles = null;
this.permissions = null;
this.shouldRenewPassword = true;
+ this.isTwoFactorAuthenticationRequired = isTwoFactorAuthenticationRequired;
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPDeliveryMethod.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPDeliveryMethod.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPDeliveryMethod.java
new file mode 100644
index 0000000..f05c7ae
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPDeliveryMethod.java
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.data;
+
+public class OTPDeliveryMethod {
+
+ private final String name;
+ private final String target;
+
+ public OTPDeliveryMethod(String name, String target) {
+ this.name = name;
+ this.target = target;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getTarget() {
+ return target;
+ }
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPMetadata.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPMetadata.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPMetadata.java
new file mode 100644
index 0000000..0365fff
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPMetadata.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.data;
+
+import org.joda.time.DateTime;
+
+public class OTPMetadata {
+
+ private final DateTime requestTime;
+ private final int tokenLiveTimeInSec;
+ private final boolean extendedAccessToken;
+ private final OTPDeliveryMethod deliveryMethod;
+
+ public OTPMetadata(DateTime requestTime, int tokenLiveTimeInSec,
+ boolean extendedAccessToken, OTPDeliveryMethod deliveryMethod) {
+ this.requestTime = requestTime;
+ this.tokenLiveTimeInSec = tokenLiveTimeInSec;
+ this.extendedAccessToken = extendedAccessToken;
+ this.deliveryMethod = deliveryMethod;
+ }
+
+ public DateTime getRequestTime() {
+ return requestTime;
+ }
+
+ public int getTokenLiveTimeInSec() {
+ return tokenLiveTimeInSec;
+ }
+
+ public boolean isExtendedAccessToken() {
+ return extendedAccessToken;
+ }
+
+ public OTPDeliveryMethod getDeliveryMethod() {
+ return deliveryMethod;
+ }
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPRequest.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPRequest.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPRequest.java
new file mode 100644
index 0000000..f9f3e24
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/OTPRequest.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.data;
+
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.joda.time.DateTime;
+
+public class OTPRequest {
+
+ private final String token;
+ private final OTPMetadata metadata;
+
+ public OTPRequest(String token, OTPMetadata metadata) {
+ this.token = token;
+ this.metadata = metadata;
+ }
+
+ public static OTPRequest create(String token, int tokenLiveTimeInSec, boolean extendedAccessToken,
+ OTPDeliveryMethod deliveryMethod) {
+ final OTPMetadata metadata = new OTPMetadata(DateUtils.getLocalDateTimeOfTenant().toDateTime(),
+ tokenLiveTimeInSec, extendedAccessToken, deliveryMethod);
+ return new OTPRequest(token, metadata);
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public OTPMetadata getMetadata() {
+ return metadata;
+ }
+
+ public boolean isValid() {
+ DateTime expireTime = metadata.getRequestTime().plusSeconds(metadata.getTokenLiveTimeInSec());
+ return DateTime.now().isBefore(expireTime);
+ }
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/TwoFactorConfigurationValidator.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/TwoFactorConfigurationValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/TwoFactorConfigurationValidator.java
new file mode 100644
index 0000000..41cf69b
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/data/TwoFactorConfigurationValidator.java
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.data;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
+import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.security.constants.TwoFactorConfigurationConstants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+
+@Component
+@Profile("twofactor")
+public class TwoFactorConfigurationValidator {
+
+ private final FromJsonHelper fromJsonHelper;
+
+ @Autowired
+ public TwoFactorConfigurationValidator(FromJsonHelper fromJsonHelper) {
+ this.fromJsonHelper = fromJsonHelper;
+ }
+
+ public void validateForUpdate(final String json) {
+ if (StringUtils.isBlank(json)) {
+ throw new InvalidJsonException();
+ }
+
+ boolean atLeastOneParameterPassedForUpdate = false;
+ final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
+ this.fromJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
+ TwoFactorConfigurationConstants.REQUEST_DATA_PARAMETERS);
+ final JsonElement element = this.fromJsonHelper.parse(json);
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
+ .resource(TwoFactorConfigurationConstants.RESOURCE_NAME);
+
+
+ for(String parameterName : TwoFactorConfigurationConstants.BOOLEAN_PARAMETERS) {
+ if(this.fromJsonHelper.parameterExists(parameterName, element)) {
+ atLeastOneParameterPassedForUpdate = true;
+ validateBooleanParameter(parameterName, element, baseDataValidator);
+ }
+ }
+
+ for(String parameterName : TwoFactorConfigurationConstants.STRING_PARAMETERS) {
+ if(this.fromJsonHelper.parameterExists(parameterName, element)) {
+ atLeastOneParameterPassedForUpdate = true;
+ validateStringParameter(parameterName, element, baseDataValidator);
+ }
+ }
+
+ for(String parameterName : TwoFactorConfigurationConstants.NUMBER_PARAMETERS) {
+ if (this.fromJsonHelper.parameterExists(parameterName, element)) {
+ atLeastOneParameterPassedForUpdate = true;
+ validateNumberParameter(parameterName, element, baseDataValidator);
+ }
+ }
+
+ if(!atLeastOneParameterPassedForUpdate) {
+ final Object forceError = null;
+ baseDataValidator.reset().anyOfNotNull(forceError);
+ }
+
+ throwExceptionIfValidationWarningsExist(dataValidationErrors);
+ }
+
+ private void throwExceptionIfValidationWarningsExist(
+ final List<ApiParameterError> dataValidationErrors) {
+ if(!dataValidationErrors.isEmpty()) {
+ throw new PlatformApiDataValidationException(dataValidationErrors);
+ }
+ }
+
+ private void validateBooleanParameter(final String name, final JsonElement element,
+ final DataValidatorBuilder baseDataValidator) {
+ final String value = this.fromJsonHelper.extractStringNamed(name, element);
+ baseDataValidator.reset().parameter(name).value(value).notNull().trueOrFalseRequired(value);
+ }
+
+ private void validateStringParameter(final String name, final JsonElement element,
+ final DataValidatorBuilder baseDataValidator) {
+ final String value = this.fromJsonHelper.extractStringNamed(name, element);
+ baseDataValidator.reset().parameter(name).value(value).notBlank().notExceedingLengthOf(1000);
+ }
+
+ private void validateNumberParameter(final String name, final JsonElement element,
+ final DataValidatorBuilder baseDataValidator) {
+ final Integer value = this.fromJsonHelper.extractIntegerSansLocaleNamed(name, element);
+ baseDataValidator.reset().parameter(name).value(value).notNull().integerGreaterThanZero();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/fineract/blob/1a966e8e/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/domain/OTPRequestRepository.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/domain/OTPRequestRepository.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/domain/OTPRequestRepository.java
new file mode 100644
index 0000000..fc11300
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/domain/OTPRequestRepository.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.security.domain;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.fineract.infrastructure.security.data.OTPRequest;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.Assert;
+
+@Repository
+@Profile("twofactor")
+public class OTPRequestRepository {
+
+ private final ConcurrentHashMap<Long, OTPRequest> OTPrequests = new ConcurrentHashMap<>();
+
+
+ public OTPRequest getOTPRequestForUser(AppUser user) {
+ Assert.notNull(user);
+
+ return this.OTPrequests.get(user.getId());
+ }
+
+ public void addOTPRequest(AppUser user, OTPRequest request) {
+ Assert.notNull(user);
+ Assert.notNull(request);
+ this.OTPrequests.put(user.getId(), request);
+ }
+
+ public void deleteOTPRequestForUser(AppUser user) {
+ this.OTPrequests.remove(user.getId());
+ }
+}