Simplify build for different auth strategies

This commit is contained in:
Aleksandar Vidakovic
2022-01-03 06:15:04 +08:00
committed by Petri Tuomola
parent c691c11699
commit 907f932abe
39 changed files with 194 additions and 331 deletions
-60
View File
@@ -1,60 +0,0 @@
name: Fineract Gradle build - oauth2
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-20.04
services:
mariad:
image: mariadb:10.6
ports:
- 3306:3306
env:
MARIADB_ROOT_PASSWORD: mysql
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
mock-oauth2-server:
image: ghcr.io/navikt/mock-oauth2-server:0.4.0
ports:
- 9000:9000
env:
SERVER_PORT: 9000
JSON_CONFIG: '{ "interactiveLogin": true, "httpServer": "NettyWrapper", "tokenCallbacks": [ { "issuerId": "auth/realms/fineract", "tokenExpiry": 120, "requestMappings": [{ "requestParam": "scope", "match": "fineract", "claims": { "sub": "mifos", "scope": [ "test" ] } } ] } ] }'
env:
TZ: Asia/Kolkata
steps:
- name: Set up cache
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-oauth2-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-oauth2-
- name: Checkout
uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'zulu'
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b
- name: Verify MariaDB connection
run: |
while ! mysqladmin ping -h"127.0.0.1" -P3306 ; do
sleep 1
done
- name: Initialise databases
run: |
./gradlew --no-daemon -q createDB -PdbName=fineract_tenants
./gradlew --no-daemon -q createDB -PdbName=fineract_default
- name: Install additional software
run: |
sudo apt-get update
sudo apt-get install ghostscript -y
- name: Build & Test
run: ./gradlew --no-daemon -q --console=plain build test --fail-fast -x :integration-tests:test -x :twofactor-tests:test -Psecurity=oauth
-52
View File
@@ -1,52 +0,0 @@
name: Fineract Gradle build - twofactor
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-20.04
services:
mariad:
image: mariadb:10.6
ports:
- 3306:3306
env:
MARIADB_ROOT_PASSWORD: mysql
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
env:
TZ: Asia/Kolkata
steps:
- name: Set up cache
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-twofactor-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-twofactor-
- name: Checkout
uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'zulu'
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b
- name: Verify MariaDB connection
run: |
while ! mysqladmin ping -h"127.0.0.1" -P3306 ; do
sleep 1
done
- name: Initialise databases
run: |
./gradlew --no-daemon -q createDB -PdbName=fineract_tenants
./gradlew --no-daemon -q createDB -PdbName=fineract_default
- name: Install additional software
run: |
sudo apt-get update
sudo apt-get install ghostscript -y
- name: Build & Test
run: ./gradlew --no-daemon -q --console=plain build test --fail-fast -x :integration-tests:test -x :oauth2-tests:test -Ptwofactor=enabled
+19 -3
View File
@@ -13,6 +13,15 @@ jobs:
env:
MARIADB_ROOT_PASSWORD: mysql
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
mock-oauth2-server:
image: ghcr.io/navikt/mock-oauth2-server:0.4.0
ports:
- 9000:9000
env:
SERVER_PORT: 9000
JSON_CONFIG: '{ "interactiveLogin": true, "httpServer": "NettyWrapper", "tokenCallbacks": [ { "issuerId": "auth/realms/fineract", "tokenExpiry": 120, "requestMappings": [{ "requestParam": "scope", "match": "fineract", "claims": { "sub": "mifos", "scope": [ "test" ] } } ] } ] }'
env:
TZ: Asia/Kolkata
steps:
@@ -22,9 +31,9 @@ jobs:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-basicauth-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-basicauth-
${{ runner.os }}-gradle-
- name: Checkout
uses: actions/checkout@v2
- name: Set up JDK 11
@@ -47,5 +56,12 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install ghostscript -y
- name: Build & Test
- name: Basic Auth Build & Test
run: ./gradlew --no-daemon -q --console=plain licenseMain licenseTest check build test --fail-fast doc -x :twofactor-tests:test -x :oauth2-test:test
- name: 2FA Build & Test
run: ./gradlew --no-daemon -q --console=plain :twofactor-tests:test --fail-fast
- name: OAuth2 Build & Test
run: ./gradlew --no-daemon -q --console=plain :oauth2-tests:test --fail-fast
-1
View File
@@ -17,7 +17,6 @@ build
catalina.base_IS_UNDEFINED/
keystore.jks
bin/
fineract-provider/src/main/resources/application.properties
.lock
fineract-provider/out/
fineract-provider/config/swagger/config.json
+44
View File
@@ -59,6 +59,50 @@ The tenants database connection details are configured [via environment variable
java -jar fineract-provider.jar
Security
============
NOTE: The HTTP Basic and OAuth2 authentication schemes are mutually exclusive. You can't enable them both at the same time. Fineract checks these settings on startup and will fail if more than one authentication scheme is enabled.
HTTP Basic Authentication
------------
By default Fineract is configured with a HTTP Basic Authentication scheme, so you actually don't have to do anything if you want to use it. But if you would like to explicitly choose this authentication scheme then there are two ways to enable it:
1. Use environment variables (best choice if you run with Docker Compose):
```
FINERACT_SECURITY_BASICAUTH_ENABLED=true
FINERACT_SECURITY_OAUTH_ENABLED=false
```
2. Use JVM parameters (best choice if you run the Spring Boot JAR):
```
java -Dfineract.security.basicauth.enabled=true -Dfineract.security.oauth.enabled=false -jar fineract-provider.jar
```
OAuth2 Authentication
------------
There is also an OAuth2 authentication scheme available. Again, two ways to enable it:
1. Use environment variables (best choice if you run with Docker Compose):
```
FINERACT_SECURITY_BASICAUTH_ENABLED=false
FINERACT_SECURITY_OAUTH_ENABLED=true
```
2. Use JVM parameters (best choice if you run the Spring Boot JAR):
```
java -Dfineract.security.basicauth.enabled=false -Dfineract.security.oauth.enabled=true -jar fineract-provider.jar
```
Two Factor Authentication
------------
You can also enable 2FA authentication. Depending on how you start Fineract add the following:
1. Use environment variable (best choice if you run with Docker Compose):
```
FINERACT_SECURITY_2FA_ENABLED=true
```
2. Use JVM parameter (best choice if you run the Spring Boot JAR):
```
-Dfineract.security.2fa.enabled=true
```
Instructions to build a WAR file
============
1. Clone the repository or download and extract the archive file to your local directory.
-32
View File
@@ -155,37 +155,6 @@ configurations {
apply from: 'dependencies.gradle'
/* Enable Oauth2 authentication based on environment, default to HTTP basic auth */
if (project.hasProperty('security') && project.getProperty('security') == 'oauth') {
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 {
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'
}
}
}
// Configuration for the modernizer plugin
// https://github.com/andygoossens/gradle-modernizer-plugin
modernizer {
@@ -324,7 +293,6 @@ jib {
]
args = [
'-Duser.home=/tmp',
'-Dspring.profiles.active=basicauth',
'-Dfile.encoding=UTF-8',
'-Duser.timezone=UTC',
'-Djava.security.egd=file:/dev/./urandom'
@@ -1,34 +0,0 @@
#
# 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
management.health.jms.enabled=false
# FINERACT 1296
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true
# FINERACT-883
management.info.git.mode=FULL
management.endpoints.web.exposure.include=health,info
# FINERACT-914
server.forward-headers-strategy=framework
@@ -1,37 +0,0 @@
#
# 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
management.health.jms.enabled=false
# FINERACT 1296
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true
# FINERACT-883
management.info.git.mode=FULL
management.endpoints.web.exposure.include=health,info
# FINERACT-914
server.forward-headers-strategy=framework
# OAuth authorisation server endpoint
spring.security.oauth2.resourceserver.jwt.issuer-uri: http://localhost:9000/auth/realms/fineract
@@ -29,10 +29,10 @@ import org.apache.fineract.infrastructure.security.filter.TwoFactorAuthenticatio
import org.apache.fineract.infrastructure.security.service.TenantAwareJpaPlatformUserDetailsService;
import org.apache.fineract.infrastructure.security.vote.SelfServiceUserAccessVote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionManager;
@@ -59,7 +59,7 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
@Configuration
@Profile("oauth")
@ConditionalOnProperty("fineract.security.oauth.enabled")
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {
@@ -23,10 +23,10 @@ import org.apache.fineract.infrastructure.security.filter.TenantAwareBasicAuthen
import org.apache.fineract.infrastructure.security.filter.TwoFactorAuthenticationFilter;
import org.apache.fineract.infrastructure.security.service.TenantAwareJpaPlatformUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
@@ -42,7 +42,7 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
@Configuration
@Profile("basicauth")
@ConditionalOnProperty("fineract.security.basicauth.enabled")
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@@ -0,0 +1,50 @@
/**
* 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.config;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SecurityValidationConfig {
@Value("${fineract.security.basicauth.enabled}")
private Boolean basicAuthEnabled;
@Value("${fineract.security.oauth.enabled}")
private Boolean oauthEnabled;
@PostConstruct
public void validate() {
// NOTE: avoid NPE if these values are not set
if (!Boolean.TRUE.equals(basicAuthEnabled) && !Boolean.TRUE.equals(oauthEnabled)) {
// NOTE: while we are already doing consistency checks we might as well cover this case; should not happen
// as defaults are set in application.properties
throw new IllegalArgumentException(
"No authentication scheme selected. Please decide if you want to use basic OR OAuth2 authentication.");
}
if (Boolean.TRUE.equals(basicAuthEnabled) && Boolean.TRUE.equals(oauthEnabled)) {
throw new IllegalArgumentException(
"Too many authentication schemes selected. Please decide if you want to use basic OR OAuth2 authentication.");
}
}
}
@@ -44,14 +44,14 @@ 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.portfolio.client.service.ClientReadPlatformService;
import org.apache.fineract.useradministration.data.RoleData;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Profile;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Scope;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
@@ -61,11 +61,14 @@ import org.springframework.stereotype.Component;
@Component
@Scope("singleton")
@Profile("basicauth")
@ConditionalOnProperty("fineract.security.basicauth.enabled")
@Path("/authentication")
@Tag(name = "Authentication HTTP Basic", description = "An API capability that allows client applications to verify authentication details using HTTP Basic Authentication.")
public class AuthenticationApiResource {
@Value("${fineract.security.2fa.enabled}")
private boolean twoFactorEnabled;
public static class AuthenticateRequest {
public String username;
@@ -75,19 +78,17 @@ public class AuthenticationApiResource {
private final DaoAuthenticationProvider customAuthenticationProvider;
private final ToApiJsonSerializer<AuthenticatedUserData> apiJsonSerializerService;
private final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext;
private final TwoFactorUtils twoFactorUtils;
private final ClientReadPlatformService clientReadPlatformService;
@Autowired
public AuthenticationApiResource(
@Qualifier("customAuthenticationProvider") final DaoAuthenticationProvider customAuthenticationProvider,
final ToApiJsonSerializer<AuthenticatedUserData> apiJsonSerializerService,
final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext, TwoFactorUtils twoFactorUtils,
final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext,
ClientReadPlatformService aClientReadPlatformService) {
this.customAuthenticationProvider = customAuthenticationProvider;
this.apiJsonSerializerService = apiJsonSerializerService;
this.springSecurityPlatformSecurityContext = springSecurityPlatformSecurityContext;
this.twoFactorUtils = twoFactorUtils;
clientReadPlatformService = aClientReadPlatformService;
}
@@ -143,7 +144,7 @@ public class AuthenticationApiResource {
final EnumOptionData organisationalRole = principal.organisationalRoleData();
boolean isTwoFactorRequired = twoFactorUtils.isTwoFactorAuthEnabled()
boolean isTwoFactorRequired = this.twoFactorEnabled
&& !principal.hasSpecificPermissionTo(TwoFactorConstants.BYPASS_TWO_FACTOR_PERMISSION);
Long userId = principal.getId();
if (this.springSecurityPlatformSecurityContext.doesPasswordHasToBeRenewed(principal)) {
@@ -45,13 +45,13 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/twofactor")
@Component
@Profile("twofactor")
@ConditionalOnProperty("fineract.security.2fa.enabled")
@Scope("singleton")
@Tag(name = "Two Factor", description = "")
public class TwoFactorApiResource {
@@ -33,7 +33,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -42,7 +42,7 @@ import org.springframework.stereotype.Component;
@Produces({ MediaType.APPLICATION_JSON })
@Component
@Scope("singleton")
@Profile("twofactor")
@ConditionalOnProperty("fineract.security.2fa.enabled")
public class TwoFactorConfigurationApiResource {
private final String resourceNameForPermissions = "TWOFACTOR_CONFIG";
@@ -37,12 +37,12 @@ import org.apache.fineract.infrastructure.security.constants.TwoFactorConstants;
import org.apache.fineract.infrastructure.security.data.AuthenticatedOauthUserData;
import org.apache.fineract.infrastructure.security.data.FineractJwtAuthenticationToken;
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;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Scope;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
@@ -54,22 +54,22 @@ import org.springframework.stereotype.Component;
*/
@Path("/userdetails")
@Component
@Profile("oauth")
@ConditionalOnProperty("fineract.security.oauth.enabled")
@Scope("singleton")
@Tag(name = "Fetch authenticated user details", description = "")
public class UserDetailsApiResource {
private final ToApiJsonSerializer<AuthenticatedOauthUserData> apiJsonSerializerService;
private final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext;
private final TwoFactorUtils twoFactorUtils;
@Value("${fineract.security.2fa.enabled}")
private boolean twoFactorEnabled;
@Autowired
public UserDetailsApiResource(final ToApiJsonSerializer<AuthenticatedOauthUserData> apiJsonSerializerService,
final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext, final TwoFactorUtils twoFactorUtils) {
final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext) {
this.apiJsonSerializerService = apiJsonSerializerService;
this.springSecurityPlatformSecurityContext = springSecurityPlatformSecurityContext;
this.twoFactorUtils = twoFactorUtils;
}
@GET
@@ -116,16 +116,15 @@ public class UserDetailsApiResource {
final EnumOptionData organisationalRole = principal.organisationalRoleData();
final boolean requireTwoFactorAuth = twoFactorUtils.isTwoFactorAuthEnabled()
boolean isTwoFactorRequired = this.twoFactorEnabled
&& !principal.hasSpecificPermissionTo(TwoFactorConstants.BYPASS_TWO_FACTOR_PERMISSION);
if (this.springSecurityPlatformSecurityContext.doesPasswordHasToBeRenewed(principal)) {
authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(), principal.getId(),
authentication.getToken().getTokenValue(), requireTwoFactorAuth);
authentication.getToken().getTokenValue(), isTwoFactorRequired);
} else {
authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(), officeId, officeName, staffId, staffDisplayName,
organisationalRole, roles, permissions, principal.getId(), authentication.getToken().getTokenValue(),
requireTwoFactorAuth);
isTwoFactorRequired);
}
return this.apiJsonSerializerService.serialize(authenticatedUserData);
@@ -43,13 +43,13 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "TWOFACTOR_ACCESSTOKEN", action = "INVALIDATE")
@Profile("twofactor")
@ConditionalOnProperty("fineract.security.2fa.enabled")
public class InvalidateTFAccessTokenCommandHandler implements NewCommandSourceHandler {
private final TwoFactorService twoFactorService;
@@ -27,13 +27,13 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "TWOFACTOR_CONFIGURATION", action = "UPDATE")
@Profile("twofactor")
@ConditionalOnProperty("fineract.security.2fa.enabled")
public class UpdateTwoFactorConfigCommandHandler implements NewCommandSourceHandler {
private final TwoFactorConfigurationService configurationService;
@@ -32,11 +32,11 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
@Component
@Profile("twofactor")
@ConditionalOnProperty("fineract.security.2fa.enabled")
public class TwoFactorConfigurationValidator {
private final FromJsonHelper fromJsonHelper;
@@ -21,12 +21,12 @@ 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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;
@Repository
@Profile("twofactor")
@ConditionalOnProperty("fineract.security.2fa.enabled")
@SuppressWarnings({ "MemberName" })
public class OTPRequestRepository {
@@ -19,11 +19,11 @@
package org.apache.fineract.infrastructure.security.domain;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.context.annotation.Profile;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@Profile("twofactor")
@ConditionalOnProperty("fineract.security.2fa.enabled")
public interface TFAccessTokenRepository extends JpaRepository<TFAccessToken, Long>, JpaSpecificationExecutor<TFAccessToken> {
TFAccessToken findByUserAndToken(AppUser user, String token);
@@ -19,11 +19,11 @@
package org.apache.fineract.infrastructure.security.domain;
import java.util.List;
import org.springframework.context.annotation.Profile;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@Profile("twofactor")
@ConditionalOnProperty("fineract.security.2fa.enabled")
public interface TwoFactorConfigurationRepository
extends JpaRepository<TwoFactorConfiguration, Long>, JpaSpecificationExecutor<TwoFactorConfiguration> {
@@ -27,7 +27,7 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.fineract.infrastructure.security.data.FineractJwtAuthenticationToken;
import org.springframework.context.annotation.Profile;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
@@ -43,7 +43,7 @@ import org.springframework.stereotype.Service;
* This filter adds 'TWOFACTOR_AUTHENTICATED' authority to every authenticated platform user.
*/
@Service
@Profile("!twofactor")
@ConditionalOnProperty(name = "fineract.security.2fa.enabled", havingValue = "false")
public class InsecureTwoFactorAuthenticationFilter extends TwoFactorAuthenticationFilter {
public InsecureTwoFactorAuthenticationFilter() {
@@ -38,7 +38,7 @@ import org.apache.fineract.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
@@ -59,7 +59,7 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi
* If multi-tenant and basic auth credentials are invalid, a http error response is returned.
*/
@Profile("basicauth")
@ConditionalOnProperty("fineract.security.basicauth.enabled")
public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFilter {
private static boolean firstRequestProcessed = false;
@@ -38,7 +38,7 @@ import org.apache.fineract.infrastructure.security.service.BasicAuthTenantDetail
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.filter.GenericFilterBean;
@@ -55,7 +55,7 @@ import org.springframework.web.filter.GenericFilterBean;
* Used to support Oauth2 authentication and the service is loaded only when "oauth" profile is active.
*/
@Service
@Profile("oauth")
@ConditionalOnProperty("fineract.security.oauth.enabled")
public class TenantAwareTenantIdentifierFilter extends GenericFilterBean {
private static boolean firstRequestProcessed = false;
@@ -34,7 +34,7 @@ import org.apache.fineract.infrastructure.security.domain.TFAccessToken;
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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
@@ -57,7 +57,7 @@ import org.springframework.web.filter.GenericFilterBean;
* authority regardless of the value of the 'Fineract-Platform-TFA-Token' header.
*/
@Service
@Profile("twofactor")
@ConditionalOnProperty("fineract.security.2fa.enabled")
public class TwoFactorAuthenticationFilter extends GenericFilterBean {
private final TwoFactorService twoFactorService;
@@ -36,13 +36,13 @@ import org.apache.fineract.infrastructure.security.domain.TwoFactorConfiguration
import org.apache.fineract.infrastructure.security.domain.TwoFactorConfigurationRepository;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("twofactor")
@ConditionalOnProperty("fineract.security.2fa.enabled")
public class TwoFactorConfigurationServiceImpl implements TwoFactorConfigurationService {
private static final String DEFAULT_EMAIL_SUBJECT = "Fineract Two-Factor Authentication Token";
@@ -39,14 +39,14 @@ import org.apache.fineract.infrastructure.sms.domain.SmsMessageRepository;
import org.apache.fineract.infrastructure.sms.scheduler.SmsMessageScheduledJobService;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("twofactor")
@ConditionalOnProperty("fineract.security.2fa.enabled")
public class TwoFactorServiceImpl implements TwoFactorService {
private final AccessTokenGenerationService accessTokenGenerationService;
@@ -1,45 +0,0 @@
/**
* 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.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class TwoFactorUtils {
private static final String TWO_FACTOR_PROFILE_NAME = "twofactor";
private final Environment environment;
@Autowired
public TwoFactorUtils(Environment environment) {
this.environment = environment;
}
public boolean isTwoFactorAuthEnabled() {
for (final String profile : this.environment.getActiveProfiles()) {
if (TWO_FACTOR_PROFILE_NAME.equals(profile)) {
return true;
}
}
return false;
}
}
@@ -31,13 +31,13 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.fineract.infrastructure.security.api.AuthenticationApiResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("singleton")
@Profile("basicauth")
@ConditionalOnProperty("fineract.security.basicauth.enabled")
@Path("/self/authentication")
@Tag(name = "Self Authentication", description = "Authenticates the credentials provided and returns the set roles and permissions allowed")
public class SelfAuthenticationApiResource {
@@ -30,13 +30,13 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.fineract.infrastructure.security.api.UserDetailsApiResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/self/userdetails")
@Component
@Profile("oauth")
@ConditionalOnProperty("fineract.security.oauth.enabled")
@Scope("singleton")
@Tag(name = "Self User Details", description = "")
@@ -17,8 +17,10 @@
# under the License.
#
spring.profiles.default=basicauth
spring.profiles.active=oauth,twofactor
fineract.security.basicauth.enabled=${FINERACT_SECURITY_BASICAUTH_ENABLED:true}
fineract.security.oauth.enabled=${FINERACT_SECURITY_OAUTH_ENABLED:false}
fineract.security.2fa.enabled=${FINERACT_SECURITY_2FA_ENABLED:false}
management.health.jms.enabled=false
# FINERACT 1296
@@ -34,4 +36,4 @@ management.endpoints.web.exposure.include=health,info
server.forward-headers-strategy=framework
# OAuth authorisation server endpoint
spring.security.oauth2.resourceserver.jwt.issuer-uri: http://localhost:9000/auth/realms/fineract
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9000/auth/realms/fineract
@@ -29,14 +29,14 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
@ExtendWith(SpringExtension.class)
@TestPropertySource("classpath:application-test.properties")
@WebAppConfiguration
@ActiveProfiles("basicauth")
@ContextConfiguration(classes = TestsWithoutDatabaseAndNoJobsConfiguration.class)
public class CommandHandlerProviderTest {
@@ -20,8 +20,8 @@ package org.apache.fineract.infrastructure.configuration.spring;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
@@ -34,8 +34,8 @@ import org.springframework.test.context.web.WebAppConfiguration;
* sufficient (as long as the TestsWithoutDatabaseAndNoJobsConfiguration used extends AbstractApplicationConfiguration).
*/
@ExtendWith(SpringExtension.class)
@TestPropertySource("classpath:application-test.properties")
@WebAppConfiguration
@ActiveProfiles("basicauth")
@ContextConfiguration(classes = TestsWithoutDatabaseAndNoJobsConfiguration.class)
public class SpringConfigurationTest {
@@ -24,6 +24,9 @@ import org.apache.fineract.infrastructure.core.service.TenantDatabaseUpgradeServ
import org.apache.fineract.infrastructure.jobs.service.JobRegisterService;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Spring @Configuration which does not require a running database. It also does not load any job configuration (as they
@@ -65,4 +68,10 @@ public class TestsWithoutDatabaseAndNoJobsConfiguration extends AbstractApplicat
DataSource mockDataSource = Mockito.mock(DataSource.class, Mockito.RETURNS_MOCKS);
return mockDataSource;
}
@Bean
@Primary
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@@ -21,14 +21,14 @@ package org.apache.fineract.infrastructure.core.boot.tests;
import org.apache.fineract.ServerWithMariaDB4jApplication;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = ServerWithMariaDB4jApplication.Configuration.class)
@TestPropertySource("classpath:application-test.properties")
@WebAppConfiguration
@ActiveProfiles("basicauth")
public abstract class AbstractSpringBootWithMariaDB4jIntegrationTest {
// do NOT put any helper methods here!
@@ -17,7 +17,10 @@
# under the License.
#
spring.profiles.default=basicauth
fineract.security.basicauth.enabled=true
fineract.security.oauth.enabled=false
fineract.security.2fa.enabled=false
management.health.jms.enabled=false
# FINERACT 1296
+1 -1
View File
@@ -45,7 +45,7 @@ cargo {
}
startStopTimeout = 240000
containerProperties {
property 'cargo.start.jvmargs', '-Dspring.profiles.active=oauth'
property 'cargo.start.jvmargs', '-Dfineract.security.basicauth.enabled=false -Dfineract.security.oauth.enabled=true -Dfineract.security.2fa.enabled=false'
property 'cargo.tomcat.connector.keystoreFile', file("$rootDir/fineract-provider/src/main/resources/keystore.jks")
property 'cargo.tomcat.connector.keystorePass', 'openmf'
property 'cargo.tomcat.httpSecure', true
+1 -1
View File
@@ -45,7 +45,7 @@ cargo {
}
startStopTimeout = 240000
containerProperties {
property 'cargo.start.jvmargs', '-Dspring.profiles.active=twofactor,basicauth'
property 'cargo.start.jvmargs', '-Dfineract.security.basicauth.enabled=true -Dfineract.security.oauth.enabled=false -Dfineract.security.2fa.enabled=true'
property 'cargo.tomcat.connector.keystoreFile', file("$rootDir/fineract-provider/src/main/resources/keystore.jks")
property 'cargo.tomcat.connector.keystorePass', 'openmf'
property 'cargo.tomcat.httpSecure', true
@@ -111,7 +111,7 @@ public class TwoFactorAuthenticationTest {
.log().ifError().when().post("/fineract-provider/api/v1/authentication?" + TENANT_IDENTIFIER).asString();
assertFalse(StringUtils.isBlank(json));
Boolean key = JsonPath.with(json).get("isTwoFactorAuthenticationRequired");
assertEquals(key, true);
assertEquals(true, key);
}
@Test