MIFOSX-1746: Add support for Oauth2 and make it build time configurable
This commit is contained in:
@@ -22,3 +22,4 @@ rebel.xml
|
|||||||
*.gradletasknamecache
|
*.gradletasknamecache
|
||||||
*~
|
*~
|
||||||
.metadata
|
.metadata
|
||||||
|
mifosng-provider\src\main\resources\application.properties
|
||||||
+377
-8
@@ -104,6 +104,8 @@
|
|||||||
<li><a href="#dates_and_numbers">Updating Dates and Numbers</a></li>
|
<li><a href="#dates_and_numbers">Updating Dates and Numbers</a></li>
|
||||||
<li><a href="#field_descriptions">Field Descriptions</a></li>
|
<li><a href="#field_descriptions">Field Descriptions</a></li>
|
||||||
<li><a href="#authentication_overview">Authentication Overview</a></li>
|
<li><a href="#authentication_overview">Authentication Overview</a></li>
|
||||||
|
<li><a href="#authentication_basicauth">Authentication HTTP Basic</a></li>
|
||||||
|
<li><a href="#authentication_oauth">Authentication Oauth2</a></li>
|
||||||
<li><a href="#errors">Errors</a></li>
|
<li><a href="#errors">Errors</a></li>
|
||||||
<li><a href="#batch_api">Batch API</a></li>
|
<li><a href="#batch_api">Batch API</a></li>
|
||||||
<li><a href="#fullapi_matrix">Full API Matrix</a></li>
|
<li><a href="#fullapi_matrix">Full API Matrix</a></li>
|
||||||
@@ -2141,13 +2143,37 @@
|
|||||||
</table></td>
|
</table></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="#authentication">Authentication</a></td>
|
<td><a href="#authenticationbasic">Authentication HTTP Basic</a></td>
|
||||||
<td>authentication</td>
|
<td>authentication</td>
|
||||||
<td><a href="#authenticate_request">Verify Authentication</a></td>
|
<td><a href="#authenticate_request_basic">Verify Authentication</a></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><a href="#authenticationoauth">Authentication Oauth2</a></td>
|
||||||
|
<td>oauth/token</td>
|
||||||
|
<td><a href="#oauth">OAuth2 Access and refresh Token Request</a></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td><a href="#oauth_access_token_req">OAuth2 Access Token Request from refresh token</a></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>authenticated user</td>
|
||||||
|
<td></td>
|
||||||
|
<td><a href="#userdetails_request">Fetch Authenticated user details</a></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="#users">User</a></td>
|
<td><a href="#users">User</a></td>
|
||||||
<td>users</td>
|
<td>users</td>
|
||||||
@@ -3039,11 +3065,14 @@
|
|||||||
<div class="method-description">
|
<div class="method-description">
|
||||||
<h3>Authentication Overview</h3>
|
<h3>Authentication Overview</h3>
|
||||||
<p>
|
<p>
|
||||||
Authentication to the API occurs via <a
|
Authentication to the API can be configured to be supported via <a
|
||||||
href="http://en.wikipedia.org/wiki/Basic_access_authentication">HTTP
|
href="http://en.wikipedia.org/wiki/Basic_access_authentication">HTTP
|
||||||
Basic Auth</a>.
|
Basic Auth</a> or <a href="http://tools.ietf.org/html/rfc6749">OAuth2</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Default authentication is using HTTP Basic Auth. Oauth2 can be enabled by using <i>-Psecurity=oauth</i> option on gradle build command.
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The platform has been configured to reject plain HTTP requests and
|
The platform has been configured to reject plain HTTP requests and
|
||||||
to expect all API requests to be made over <a
|
to expect all API requests to be made over <a
|
||||||
@@ -3053,6 +3082,26 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="method-example">
|
<div class="method-example">
|
||||||
<code>
|
<code>
|
||||||
|
For eg.
|
||||||
|
Use 'gradlew clean tomcatRunWar' for HTTP Basic Auth and use 'gradlew clean tomcatRunWar -Psecurity=oauth' for Oauth2 Authentication.
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="authentication_basicauth" name="authentication_basicauth"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h3>Authentication HTTP Basic</h3>
|
||||||
|
<p>
|
||||||
|
Authentication to the API occurs via <a
|
||||||
|
href="http://en.wikipedia.org/wiki/Basic_access_authentication">HTTP
|
||||||
|
Basic Auth</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code>
|
||||||
// A Javascript/Jquery example of how to login to Mifos X and use its api.
|
// A Javascript/Jquery example of how to login to Mifos X and use its api.
|
||||||
// Typically, the javascript application would
|
// Typically, the javascript application would
|
||||||
// 1) Display a login page to retrieve the username and password.
|
// 1) Display a login page to retrieve the username and password.
|
||||||
@@ -3098,6 +3147,63 @@ function executeAjaxRequest(url, verbType, jsonData, basicAuthKey, successFuncti
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<a id="authentication_oauth" name="authentication_oauth"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h3>Authentication Oauth2</h3>
|
||||||
|
<p>
|
||||||
|
Authentication to the API occurs via <a href="http://tools.ietf.org/html/rfc6749">OAuth2</a>. 'Resource Owner Password Credentials Grant' type is used.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code>
|
||||||
|
// A Javascript/Jquery example of how to login to Mifos X and use its api.
|
||||||
|
// Typically, the javascript application would
|
||||||
|
// 1) Display a login page to retrieve the username and password.
|
||||||
|
// 2) Send the username, password, client_id , grant_type and client_secret to a function
|
||||||
|
// such as getOauthToken below which sets the HTTP bearer Auth key.
|
||||||
|
// 3) The HTTP bearer Auth key is used in all subsequent requests
|
||||||
|
// (see the function executeAjaxRequest below).
|
||||||
|
|
||||||
|
function getOauthToken(username, password) {
|
||||||
|
|
||||||
|
var jqxhr = $.ajax({
|
||||||
|
url : "/mifosng-provider/api/oauth/token?username=" + credentials.username + "&password=" + credentials.password +"&client_id=community-app&grant_type=password&client_secret=123,
|
||||||
|
type : 'POST',
|
||||||
|
contentType : "application/json; charset=utf-8",
|
||||||
|
dataType : 'json',
|
||||||
|
data : "{}",
|
||||||
|
cache : false,
|
||||||
|
success : function(data, textStatus, jqXHR) {
|
||||||
|
authKey = data.access_token;
|
||||||
|
},
|
||||||
|
error : function(jqXHR, textStatus, errorThrown) {
|
||||||
|
//error processing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeAjaxRequest(url, verbType, jsonData, authKey, successFunction, errorFunction) {
|
||||||
|
var jqxhr = $.ajax({
|
||||||
|
url : url,
|
||||||
|
type : verbType, //POST, GET, PUT or DELETE
|
||||||
|
contentType : "application/json; charset=utf-8",
|
||||||
|
dataType : 'json',
|
||||||
|
data : jsonData,
|
||||||
|
cache : false,
|
||||||
|
beforeSend : function(xhr) {
|
||||||
|
xhr.setRequestHeader("Authorization", "bearer " + authKey);
|
||||||
|
},
|
||||||
|
success : successFunction,
|
||||||
|
error : errorFunction
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a id="batch_api" name="batch_api" class="old-syle-anchor"> </a>
|
<a id="batch_api" name="batch_api" class="old-syle-anchor"> </a>
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
<div class="method-description">
|
<div class="method-description">
|
||||||
@@ -30584,12 +30690,12 @@ GET https://DomainName/api/v1/runreports/{reportName}
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- start of authentication api docs -->
|
<!-- start of authentication api docs -->
|
||||||
<a id="authentication" name="authentication" class="old-syle-anchor"> </a>
|
<a id="authenticationbasic" name="authenticationbasic" class="old-syle-anchor"> </a>
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
<div class="method-description">
|
<div class="method-description">
|
||||||
<h3>Authentication</h3>
|
<h3>Authentication HTTP Basic</h3>
|
||||||
<p>An API capability that allows client applications to verify
|
<p>An API capability that allows client applications to verify
|
||||||
authentication details.</p>
|
authentication details using HTTP Basic Authentication.</p>
|
||||||
<table class=matrixHeading>
|
<table class=matrixHeading>
|
||||||
<tr class="matrixHeadingBG">
|
<tr class="matrixHeadingBG">
|
||||||
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
||||||
@@ -30608,7 +30714,7 @@ GET https://DomainName/api/v1/runreports/{reportName}
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a id="authenticate_request" name="authenticate_request" class="old-syle-anchor"> </a>
|
<a id="authenticate_request_basic" name="authenticate_request_basic" class="old-syle-anchor"> </a>
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
<div class="method-description">
|
<div class="method-description">
|
||||||
<h4>Verify authentication</h4>
|
<h4>Verify authentication</h4>
|
||||||
@@ -30695,6 +30801,269 @@ No Request Body
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<a id="authenticationoauth" name="authenticationoauth" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h3>Authentication Oauth2</h3>
|
||||||
|
<p>An API capability that allows client applications to fetch current user details details using Oauth2.</p>
|
||||||
|
<table class=matrixHeading>
|
||||||
|
<tr class="matrixHeadingBG">
|
||||||
|
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>accessToken</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>HTTP Auth bearer key. See <a
|
||||||
|
href="#authentication_overview">Authentication Overview</a> for
|
||||||
|
an example of its use.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="oauth" name="oauth" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h3>Authentication via OAuth2</h3>
|
||||||
|
<p>API for requesting OAuth2 access token and refresh token.</p>
|
||||||
|
<table class=matrixHeading>
|
||||||
|
<tr class="matrixHeadingBG">
|
||||||
|
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>grant_type</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Mandatory field.
|
||||||
|
Indicates the requested grant type. Supported values:
|
||||||
|
<span>password</span>.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>client_id</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Mandatory field.
|
||||||
|
Indicates the client application identity.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>client_secret</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Optional field.
|
||||||
|
Indicates the client application password.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>username</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Mandatory field.
|
||||||
|
Application User(resource) login name.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>password</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Mandatory field.
|
||||||
|
Application User(resource) password.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="oauth_request" name="oauth_request" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>OAuth2 Refresh and Access Token Request</h4>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/oauth/token?username={username}&password={password}&client_id={clientId}&grant_type={grant_type}&client_secret={client_secret}
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST api/oauth/token?username=mifos&password=password&client_id=community-app&grant_type=password&client_secret=123
|
||||||
|
Content-Type: application/json
|
||||||
|
No Request Body
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
""access_token": "b771987f-82fc-45ba-b521-bfe280c4e603",
|
||||||
|
"token_type": "bearer",
|
||||||
|
"refresh_token":"a2a89b23-8d22-4d90-8585-8f464db433b0",
|
||||||
|
"expires_in": 3599,
|
||||||
|
"scope": "all"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<a id="oauth_access_token_req" name="oauth_access_token_req" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h3>Authentication via OAuth2</h3>
|
||||||
|
<p>API for requesting OAuth2 access tokens through oAuth2 refresh tokens.</p>
|
||||||
|
<table class=matrixHeading>
|
||||||
|
<tr class="matrixHeadingBG">
|
||||||
|
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>grant_type</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Mandatory field.
|
||||||
|
Indicates the requested grant type. Supported values:
|
||||||
|
<span>refresh_token</span>.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>client_id</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Mandatory field.
|
||||||
|
Indicates the client application identity.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>client_secret</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Optional field.
|
||||||
|
Indicates the client application password.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>refresh_token</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Mandatory field.
|
||||||
|
Application refresh token to generate access token.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="oauth_request_with_refresh" name="oauth_request_with_refresh" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>OAuth2 Token Request through Refresh Token</h4>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/oauth/token?refresh_token={refresh_token}&client_id={clientId}&grant_type={grant_type}&client_secret={client_secret}
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST api/oauth/token?client_id=community-app&grant_type=refresh_token&client_secret=123&refresh_token=a2a89b23-8d22-4d90-8585-8f464db433b0
|
||||||
|
Content-Type: application/json
|
||||||
|
No Request Body
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
""access_token": "b771987f-82fc-45ba-b521-bfe280c4e643",
|
||||||
|
"token_type": "bearer",
|
||||||
|
"refresh_token":"a2a89b23-8d22-4d90-8585-8f464db433b0",
|
||||||
|
"expires_in": 3599,
|
||||||
|
"scope": "all"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="userdetails_request" name="userdetails_request" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Fetch authenticated user details </h4>
|
||||||
|
<p>checks the Authentication and returns the set roles and permissions allowed.</p>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/v1/userdetails?access_token={access_token}
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST userdetails?access_token=bWlmb3M6cGFzc3dvcmQ=
|
||||||
|
Content-Type: application/json
|
||||||
|
No Request Body
|
||||||
|
</code>
|
||||||
|
<p>Example response of authenticated user user that is not linked with any staff.</p>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"username": "mifos",
|
||||||
|
"userId": 1,
|
||||||
|
"accessToken": "bWlmb3M6cGFzc3dvcmQ=",
|
||||||
|
"authenticated": true,
|
||||||
|
"officeId": 1,
|
||||||
|
"officeName": "Head Office",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Super user",
|
||||||
|
"description": "This role provides all application permissions."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
"ALL_FUNCTIONS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<p>Example response of authenticated user that is linked with a staff member and role.</p>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"username": "mifos",
|
||||||
|
"userId": 1,
|
||||||
|
"accessToken": "bWlmb3M6cGFzc3dvcmQ=",
|
||||||
|
"authenticated": true,
|
||||||
|
"officeId": 1,
|
||||||
|
"officeName": "Head Office",
|
||||||
|
"staffId": 1,
|
||||||
|
"staffDisplayName": "Director, Program",
|
||||||
|
"organisationalRole": {
|
||||||
|
"id": 100,
|
||||||
|
"code": "staffOrganisationalRoleType.programDirector",
|
||||||
|
"value": "Program Director"
|
||||||
|
},
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Super user",
|
||||||
|
"description": "This role provides all application permissions."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
"ALL_FUNCTIONS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<code class="method-request">
|
||||||
|
POST api/oauth/token?username=mifos&password=password&client_id=community-app&grant_type=password&client_secret=123
|
||||||
|
Content-Type: application/json
|
||||||
|
No Request Body
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"developerMessage": "Invalid authentication details were passed in api request.",
|
||||||
|
"developerDocLink": "https://github.com/openMF/mifosx/wiki/HTTP-API-Error-codes",
|
||||||
|
"httpStatusCode": "401",
|
||||||
|
"defaultUserMessage": "Unauthenticated. Please login.",
|
||||||
|
"userMessageGlobalisationCode": "error.msg.not.authenticated",
|
||||||
|
"errors": []
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- start of users api docs -->
|
<!-- start of users api docs -->
|
||||||
<a id="users" name="users" class="old-syle-anchor"> </a>
|
<a id="users" name="users" class="old-syle-anchor"> </a>
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ targetCompatibility = JavaVersion.VERSION_1_7
|
|||||||
|
|
||||||
project.ext.springBootVersion = '1.1.6.RELEASE'
|
project.ext.springBootVersion = '1.1.6.RELEASE'
|
||||||
project.ext.springVersion = '4.0.7.RELEASE'
|
project.ext.springVersion = '4.0.7.RELEASE'
|
||||||
|
project.ext.springOauthVersion = '2.0.4.RELEASE'
|
||||||
project.ext.jerseyVersion = '1.17'
|
project.ext.jerseyVersion = '1.17'
|
||||||
project.ext.springDataJpaVersion = '1.7.0.RELEASE' // also change spring-boot-gradle-plugin version above
|
project.ext.springDataJpaVersion = '1.7.0.RELEASE' // also change spring-boot-gradle-plugin version above
|
||||||
|
|
||||||
@@ -64,6 +65,21 @@ if (project.hasProperty('env') && project.getProperty('env') == 'dev') {
|
|||||||
apply from: 'dependencies.gradle'
|
apply from: 'dependencies.gradle'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copy {
|
||||||
|
from './properties/basicauth/'
|
||||||
|
into 'src/main/resources/'
|
||||||
|
include '*.properties'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
task dist(type:Zip){
|
task dist(type:Zip){
|
||||||
baseName = 'mifosplatform'
|
baseName = 'mifosplatform'
|
||||||
version = qualifyVersionIfNecessary(releaseVersion)
|
version = qualifyVersionIfNecessary(releaseVersion)
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ dependencies {
|
|||||||
[group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion],
|
[group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion],
|
||||||
|
|
||||||
[group: 'org.springframework', name: 'spring-context-support', version: springVersion],
|
[group: 'org.springframework', name: 'spring-context-support', version: springVersion],
|
||||||
|
|
||||||
|
[group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: springOauthVersion],
|
||||||
|
|
||||||
[group: 'javax.ws.rs', name: 'jsr311-api', version: '1.1.1'],
|
[group: 'javax.ws.rs', name: 'jsr311-api', version: '1.1.1'],
|
||||||
[group: 'com.sun.jersey', name: 'jersey-core', version: jerseyVersion],
|
[group: 'com.sun.jersey', name: 'jersey-core', version: jerseyVersion],
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ dependencies {
|
|||||||
[group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion],
|
[group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion],
|
||||||
|
|
||||||
[group: 'org.springframework', name: 'spring-context-support', version: springVersion],
|
[group: 'org.springframework', name: 'spring-context-support', version: springVersion],
|
||||||
|
[group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: springOauthVersion],
|
||||||
|
|
||||||
[group: 'javax.ws.rs', name: 'jsr311-api', version: '1.1.1'],
|
[group: 'javax.ws.rs', name: 'jsr311-api', version: '1.1.1'],
|
||||||
[group: 'com.sun.jersey', name: 'jersey-core', version: jerseyVersion],
|
[group: 'com.sun.jersey', name: 'jersey-core', version: jerseyVersion],
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
#
|
||||||
|
|
||||||
|
spring.profiles.default=basicauth
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
#
|
||||||
|
|
||||||
|
spring.profiles.default=basicauth
|
||||||
|
spring.profiles.active=oauth
|
||||||
+1
-1
@@ -26,7 +26,7 @@ import org.springframework.context.annotation.ImportResource;
|
|||||||
* and MariaDB4j (because those differ in the subclasses).
|
* and MariaDB4j (because those differ in the subclasses).
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import({ WebXmlConfiguration.class, WebFrontEndConfiguration.class })
|
@Import({ WebXmlConfiguration.class, WebXmlOauthConfiguration.class, WebFrontEndConfiguration.class })
|
||||||
@ImportResource({ "classpath*:META-INF/spring/appContext.xml" })
|
@ImportResource({ "classpath*:META-INF/spring/appContext.xml" })
|
||||||
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class,
|
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class,
|
||||||
HibernateJpaAutoConfiguration.class,
|
HibernateJpaAutoConfiguration.class,
|
||||||
|
|||||||
+2
@@ -15,6 +15,7 @@ import org.springframework.boot.context.embedded.FilterRegistrationBean;
|
|||||||
import org.springframework.boot.context.embedded.ServletRegistrationBean;
|
import org.springframework.boot.context.embedded.ServletRegistrationBean;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||||
|
|
||||||
import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
|
import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
|
||||||
@@ -26,6 +27,7 @@ import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
|
|||||||
* href="http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-convert-an-existing-application-to-spring-boot">#howto-convert-an-existing-application-to-spring-boot</a>
|
* href="http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-convert-an-existing-application-to-spring-boot">#howto-convert-an-existing-application-to-spring-boot</a>
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@Profile("basicauth")
|
||||||
public class WebXmlConfiguration {
|
public class WebXmlConfiguration {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.core.boot;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import javax.servlet.Servlet;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.embedded.ServletRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||||
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
|
import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Configuration replaces what formerly was in web.xml. Beans are loaded only when "oauth" Profile is enabled.
|
||||||
|
*
|
||||||
|
* @see <a
|
||||||
|
* href="http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-convert-an-existing-application-to-spring-boot">#howto-convert-an-existing-application-to-spring-boot</a>
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@Profile("oauth")
|
||||||
|
public class WebXmlOauthConfiguration {
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Filter springSecurityFilterChain() {
|
||||||
|
return new DelegatingFilterProxy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ServletRegistrationBean jersey() {
|
||||||
|
Servlet jerseyServlet = new SpringServlet();
|
||||||
|
ServletRegistrationBean jerseyServletRegistration = new ServletRegistrationBean();
|
||||||
|
jerseyServletRegistration.setServlet(jerseyServlet);
|
||||||
|
jerseyServletRegistration.addUrlMappings("/api/v1/*");
|
||||||
|
jerseyServletRegistration.setName("jersey-servlet");
|
||||||
|
jerseyServletRegistration.setLoadOnStartup(1);
|
||||||
|
jerseyServletRegistration.addInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
|
||||||
|
// jerseyServletRegistration.addInitParameter("com.sun.jersey.spi.container.ContainerResponseFilters",
|
||||||
|
// ResponseCorsFilter.class.getName());
|
||||||
|
jerseyServletRegistration.addInitParameter("com.sun.jersey.config.feature.DisableWADL", "true");
|
||||||
|
// debugging for development:
|
||||||
|
// jerseyServletRegistration.addInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters",
|
||||||
|
// LoggingFilter.class.getName());
|
||||||
|
return jerseyServletRegistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) {
|
||||||
|
ServletRegistrationBean registrationBean = new ServletRegistrationBean(dispatcherServlet);
|
||||||
|
registrationBean.addUrlMappings("/api/oauth/token");
|
||||||
|
return registrationBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+2
@@ -24,6 +24,7 @@ import org.mifosplatform.useradministration.domain.AppUser;
|
|||||||
import org.mifosplatform.useradministration.domain.Role;
|
import org.mifosplatform.useradministration.domain.Role;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
@@ -35,6 +36,7 @@ import com.sun.jersey.core.util.Base64;
|
|||||||
|
|
||||||
@Path("/authentication")
|
@Path("/authentication")
|
||||||
@Component
|
@Component
|
||||||
|
@Profile("basicauth")
|
||||||
@Scope("singleton")
|
@Scope("singleton")
|
||||||
public class AuthenticationApiResource {
|
public class AuthenticationApiResource {
|
||||||
|
|
||||||
|
|||||||
+98
@@ -0,0 +1,98 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.security.api;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||||
|
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
|
||||||
|
import org.mifosplatform.infrastructure.security.data.AuthenticatedOauthUserData;
|
||||||
|
import org.mifosplatform.infrastructure.security.service.SpringSecurityPlatformSecurityContext;
|
||||||
|
import org.mifosplatform.useradministration.data.RoleData;
|
||||||
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
|
import org.mifosplatform.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.context.annotation.Scope;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implementation of Oauth2 authentication APIs, loaded only when "oauth" profile is enabled.
|
||||||
|
*/
|
||||||
|
@Path("/userdetails")
|
||||||
|
@Component
|
||||||
|
@Profile("oauth")
|
||||||
|
@Scope("singleton")
|
||||||
|
public class UserDetailsApiResource {
|
||||||
|
|
||||||
|
private final ResourceServerTokenServices tokenServices;
|
||||||
|
private final ToApiJsonSerializer<AuthenticatedOauthUserData> apiJsonSerializerService;
|
||||||
|
private final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public UserDetailsApiResource(@Qualifier("tokenServices") final ResourceServerTokenServices tokenServices,
|
||||||
|
final ToApiJsonSerializer<AuthenticatedOauthUserData> apiJsonSerializerService,
|
||||||
|
final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext) {
|
||||||
|
this.tokenServices = tokenServices;
|
||||||
|
this.apiJsonSerializerService = apiJsonSerializerService;
|
||||||
|
this.springSecurityPlatformSecurityContext = springSecurityPlatformSecurityContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
public String fetchAuthenticatedUserData(@QueryParam("access_token") final String accessToken) {
|
||||||
|
|
||||||
|
final Authentication authentication = this.tokenServices.loadAuthentication(accessToken);
|
||||||
|
if (authentication.isAuthenticated()) {
|
||||||
|
final AppUser principal = (AppUser) authentication.getPrincipal();
|
||||||
|
|
||||||
|
final Collection<String> permissions = new ArrayList<>();
|
||||||
|
AuthenticatedOauthUserData authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(), permissions);
|
||||||
|
|
||||||
|
final Collection<GrantedAuthority> authorities = new ArrayList<>(authentication.getAuthorities());
|
||||||
|
for (final GrantedAuthority grantedAuthority : authorities) {
|
||||||
|
permissions.add(grantedAuthority.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
final Collection<RoleData> roles = new ArrayList<>();
|
||||||
|
final Set<Role> userRoles = principal.getRoles();
|
||||||
|
for (final Role role : userRoles) {
|
||||||
|
roles.add(role.toData());
|
||||||
|
}
|
||||||
|
|
||||||
|
final Long officeId = principal.getOffice().getId();
|
||||||
|
final String officeName = principal.getOffice().getName();
|
||||||
|
|
||||||
|
final Long staffId = principal.getStaffId();
|
||||||
|
final String staffDisplayName = principal.getStaffDisplayName();
|
||||||
|
|
||||||
|
final EnumOptionData organisationalRole = principal.organisationalRoleData();
|
||||||
|
|
||||||
|
if (this.springSecurityPlatformSecurityContext.doesPasswordHasToBeRenewed(principal)) {
|
||||||
|
authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(), principal.getId(), accessToken);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
authenticatedUserData = new AuthenticatedOauthUserData(principal.getUsername(), officeId, officeName, staffId, staffDisplayName,
|
||||||
|
organisationalRole, roles, permissions, principal.getId(), accessToken);
|
||||||
|
}
|
||||||
|
return this.apiJsonSerializerService.serialize(authenticatedUserData);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.security.data;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||||
|
import org.mifosplatform.useradministration.data.RoleData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immutable data object for authentication. Used in case of Oauth2.
|
||||||
|
*/
|
||||||
|
public class AuthenticatedOauthUserData {
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final String username;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final Long userId;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final String accessToken;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final boolean authenticated;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final Long officeId;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final String officeName;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final Long staffId;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final String staffDisplayName;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final EnumOptionData organisationalRole;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final Collection<RoleData> roles;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final Collection<String> permissions;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final boolean shouldRenewPassword;
|
||||||
|
|
||||||
|
public AuthenticatedOauthUserData(final String username, final Collection<String> permissions) {
|
||||||
|
this.username = username;
|
||||||
|
this.userId = null;
|
||||||
|
this.accessToken = null;
|
||||||
|
this.authenticated = false;
|
||||||
|
this.officeId = null;
|
||||||
|
this.officeName = null;
|
||||||
|
this.staffId = null;
|
||||||
|
this.staffDisplayName = null;
|
||||||
|
this.organisationalRole = null;
|
||||||
|
this.roles = null;
|
||||||
|
this.permissions = permissions;
|
||||||
|
this.shouldRenewPassword = 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) {
|
||||||
|
this.username = username;
|
||||||
|
this.officeId = officeId;
|
||||||
|
this.officeName = officeName;
|
||||||
|
this.staffId = staffId;
|
||||||
|
this.staffDisplayName = staffDisplayName;
|
||||||
|
this.organisationalRole = organisationalRole;
|
||||||
|
this.userId = userId;
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.authenticated = true;
|
||||||
|
this.roles = roles;
|
||||||
|
this.permissions = permissions;
|
||||||
|
this.shouldRenewPassword = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticatedOauthUserData(final String username, final Long userId, final String accessToken) {
|
||||||
|
this.username = username;
|
||||||
|
this.officeId = null;
|
||||||
|
this.officeName = null;
|
||||||
|
this.staffId = null;
|
||||||
|
this.staffDisplayName = null;
|
||||||
|
this.organisationalRole = null;
|
||||||
|
this.userId = userId;
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.authenticated = true;
|
||||||
|
this.roles = null;
|
||||||
|
this.permissions = null;
|
||||||
|
this.shouldRenewPassword = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
@@ -27,6 +27,7 @@ import org.mifosplatform.infrastructure.security.service.BasicAuthTenantDetailsS
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
@@ -49,6 +50,7 @@ import org.springframework.stereotype.Service;
|
|||||||
* is returned.
|
* is returned.
|
||||||
*/
|
*/
|
||||||
@Service(value = "basicAuthenticationProcessingFilter")
|
@Service(value = "basicAuthenticationProcessingFilter")
|
||||||
|
@Profile("basicauth")
|
||||||
public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFilter {
|
public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFilter {
|
||||||
|
|
||||||
private static boolean firstRequestProcessed = false;
|
private static boolean firstRequestProcessed = false;
|
||||||
|
|||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.security.filter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.time.StopWatch;
|
||||||
|
import org.mifosplatform.infrastructure.cache.domain.CacheType;
|
||||||
|
import org.mifosplatform.infrastructure.cache.service.CacheWritePlatformService;
|
||||||
|
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||||
|
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
|
||||||
|
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
|
||||||
|
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
|
import org.mifosplatform.infrastructure.security.data.PlatformRequestLog;
|
||||||
|
import org.mifosplatform.infrastructure.security.exception.InvalidTenantIdentiferException;
|
||||||
|
import org.mifosplatform.infrastructure.security.service.BasicAuthTenantDetailsService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.filter.GenericFilterBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This filter is responsible for extracting multi-tenant from the request and
|
||||||
|
* setting Cross-Origin details to response.
|
||||||
|
*
|
||||||
|
* If multi-tenant are valid, the details of the tenant are stored in
|
||||||
|
* {@link MifosPlatformTenant} and stored in a {@link ThreadLocal} variable for
|
||||||
|
* this request using {@link ThreadLocalContextUtil}.
|
||||||
|
*
|
||||||
|
* If multi-tenant are invalid, a http error response is returned.
|
||||||
|
*
|
||||||
|
* Used to support Oauth2 authentication and the service is loaded only when "oauth" profile is active.
|
||||||
|
*/
|
||||||
|
@Service(value = "tenantIdentifierProcessingFilter")
|
||||||
|
@Profile("oauth")
|
||||||
|
public class TenantAwareTenantIdentifierFilter extends GenericFilterBean {
|
||||||
|
|
||||||
|
private static boolean firstRequestProcessed = false;
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(TenantAwareTenantIdentifierFilter.class);
|
||||||
|
|
||||||
|
private final BasicAuthTenantDetailsService basicAuthTenantDetailsService;
|
||||||
|
private final ToApiJsonSerializer<PlatformRequestLog> toApiJsonSerializer;
|
||||||
|
private final ConfigurationDomainService configurationDomainService;
|
||||||
|
private final CacheWritePlatformService cacheWritePlatformService;
|
||||||
|
|
||||||
|
private final String tenantRequestHeader = "X-Mifos-Platform-TenantId";
|
||||||
|
private final boolean exceptionIfHeaderMissing = true;
|
||||||
|
private final String apiUri = "/api/v1/";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public TenantAwareTenantIdentifierFilter(final BasicAuthTenantDetailsService basicAuthTenantDetailsService,
|
||||||
|
final ToApiJsonSerializer<PlatformRequestLog> toApiJsonSerializer, final ConfigurationDomainService configurationDomainService,
|
||||||
|
final CacheWritePlatformService cacheWritePlatformService) {
|
||||||
|
this.basicAuthTenantDetailsService = basicAuthTenantDetailsService;
|
||||||
|
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||||
|
this.configurationDomainService = configurationDomainService;
|
||||||
|
this.cacheWritePlatformService = cacheWritePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {
|
||||||
|
|
||||||
|
final HttpServletRequest request = (HttpServletRequest) req;
|
||||||
|
final HttpServletResponse response = (HttpServletResponse) res;
|
||||||
|
|
||||||
|
final StopWatch task = new StopWatch();
|
||||||
|
task.start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
// allows for Cross-Origin
|
||||||
|
// Requests (CORs) to be performed against the platform API.
|
||||||
|
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||||
|
final String reqHead = request.getHeader("Access-Control-Request-Headers");
|
||||||
|
|
||||||
|
if (null != reqHead && !reqHead.equals(null)) {
|
||||||
|
response.setHeader("Access-Control-Allow-Headers", reqHead);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
|
||||||
|
|
||||||
|
String tenantIdentifier = request.getHeader(this.tenantRequestHeader);
|
||||||
|
if (org.apache.commons.lang.StringUtils.isBlank(tenantIdentifier)) {
|
||||||
|
tenantIdentifier = request.getParameter("tenantIdentifier");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tenantIdentifier == null && this.exceptionIfHeaderMissing) { throw new InvalidTenantIdentiferException(
|
||||||
|
"No tenant identifier found: Add request header of '" + this.tenantRequestHeader
|
||||||
|
+ "' or add the parameter 'tenantIdentifier' to query string of request URL."); }
|
||||||
|
|
||||||
|
String pathInfo = request.getRequestURI();
|
||||||
|
boolean isReportRequest = false;
|
||||||
|
if (pathInfo != null && pathInfo.contains("report")) {
|
||||||
|
isReportRequest = true;
|
||||||
|
}
|
||||||
|
final MifosPlatformTenant tenant = this.basicAuthTenantDetailsService.loadTenantById(tenantIdentifier, isReportRequest);
|
||||||
|
|
||||||
|
ThreadLocalContextUtil.setTenant(tenant);
|
||||||
|
String authToken = request.getHeader("Authorization");
|
||||||
|
|
||||||
|
if (authToken != null && authToken.startsWith("bearer ")) {
|
||||||
|
ThreadLocalContextUtil.setAuthToken(authToken.replaceFirst("bearer ", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!firstRequestProcessed) {
|
||||||
|
final String baseUrl = request.getRequestURL().toString()
|
||||||
|
.replace(request.getRequestURI(), request.getContextPath() + apiUri);
|
||||||
|
System.setProperty("baseUrl", baseUrl);
|
||||||
|
|
||||||
|
final boolean ehcacheEnabled = this.configurationDomainService.isEhcacheEnabled();
|
||||||
|
if (ehcacheEnabled) {
|
||||||
|
this.cacheWritePlatformService.switchToCache(CacheType.SINGLE_NODE);
|
||||||
|
} else {
|
||||||
|
this.cacheWritePlatformService.switchToCache(CacheType.NO_CACHE);
|
||||||
|
}
|
||||||
|
TenantAwareTenantIdentifierFilter.firstRequestProcessed = true;
|
||||||
|
}
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
} catch (final InvalidTenantIdentiferException e) {
|
||||||
|
// deal with exception at low level
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(null);
|
||||||
|
|
||||||
|
response.addHeader("WWW-Authenticate", "Basic realm=\"" + "Mifos Platform API" + "\"");
|
||||||
|
response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
|
||||||
|
} finally {
|
||||||
|
task.stop();
|
||||||
|
final PlatformRequestLog log = PlatformRequestLog.from(task, request);
|
||||||
|
logger.info(this.toApiJsonSerializer.serialize(log));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,55 +11,210 @@
|
|||||||
You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
<beans:beans xmlns="http://www.springframework.org/schema/security"
|
<beans:beans xmlns="http://www.springframework.org/schema/security"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
|
||||||
xsi:schemaLocation="
|
xsi:schemaLocation="
|
||||||
http://www.springframework.org/schema/security
|
http://www.springframework.org/schema/beans
|
||||||
http://www.springframework.org/schema/security/spring-security-3.2.xsd
|
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
|
||||||
http://www.springframework.org/schema/beans
|
http://www.springframework.org/schema/security/oauth2
|
||||||
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
|
http://www.springframework.org/schema/security/spring-security-oauth2.xsd
|
||||||
|
http://www.springframework.org/schema/security
|
||||||
|
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
|
||||||
|
|
||||||
<http create-session="stateless" use-expressions="true" pattern="/api/**"
|
<beans:beans profile="basicauth">
|
||||||
entry-point-ref="basicAuthenticationEntryPoint">
|
<http create-session="stateless" use-expressions="true" pattern="/api/**"
|
||||||
<intercept-url pattern="/api/*/authentication" access="permitAll"
|
entry-point-ref="basicAuthenticationEntryPoint">
|
||||||
method="POST" requires-channel="https" />
|
<intercept-url pattern="/api/*/authentication" access="permitAll"
|
||||||
<intercept-url pattern="/api/**" access="isFullyAuthenticated()"
|
method="POST" requires-channel="https" />
|
||||||
method="GET" requires-channel="https" />
|
<intercept-url pattern="/api/**" access="isFullyAuthenticated()"
|
||||||
<intercept-url pattern="/api/**" access="isFullyAuthenticated()"
|
method="GET" requires-channel="https" />
|
||||||
method="POST" requires-channel="https" />
|
<intercept-url pattern="/api/**" access="isFullyAuthenticated()"
|
||||||
<intercept-url pattern="/api/**" access="isFullyAuthenticated()"
|
method="POST" requires-channel="https" />
|
||||||
method="PUT" requires-channel="https" />
|
<intercept-url pattern="/api/**" access="isFullyAuthenticated()"
|
||||||
<intercept-url pattern="/api/**" access="isFullyAuthenticated()"
|
method="PUT" requires-channel="https" />
|
||||||
method="DELETE" requires-channel="https" />
|
<intercept-url pattern="/api/**" access="isFullyAuthenticated()"
|
||||||
<intercept-url pattern="/api/**" access="isFullyAuthenticated()"
|
method="DELETE" requires-channel="https" />
|
||||||
method="HEAD" requires-channel="https" />
|
<intercept-url pattern="/api/**" access="isFullyAuthenticated()"
|
||||||
|
method="HEAD" requires-channel="https" />
|
||||||
|
|
||||||
|
<custom-filter after="SECURITY_CONTEXT_FILTER"
|
||||||
|
ref="basicAuthenticationProcessingFilter" />
|
||||||
|
</http>
|
||||||
|
|
||||||
|
<beans:bean id="basicAuthenticationEntryPoint"
|
||||||
|
class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
|
||||||
|
<beans:property name="realmName" value="Mifos Platform API" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="passwordEncoder"
|
||||||
|
class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
|
||||||
|
<beans:constructor-arg value="256" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="saltSource"
|
||||||
|
class="org.springframework.security.authentication.dao.ReflectionSaltSource">
|
||||||
|
<beans:property name="userPropertyToUse" value="id" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="customAuthenticationProvider"
|
||||||
|
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
||||||
|
<beans:property name="userDetailsService" ref="userDetailsService" />
|
||||||
|
<beans:property name="passwordEncoder" ref="passwordEncoder" />
|
||||||
|
<beans:property name="saltSource" ref="saltSource" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<authentication-manager alias="authenticationManager"
|
||||||
|
erase-credentials="false">
|
||||||
|
<authentication-provider ref="customAuthenticationProvider" />
|
||||||
|
</authentication-manager>
|
||||||
|
</beans:beans>
|
||||||
|
<beans:beans profile="oauth">
|
||||||
|
<http create-session="stateless" use-expressions="true" pattern="/api/v1/**"
|
||||||
|
entry-point-ref="oauthAuthenticationEntryPoint"
|
||||||
|
access-decision-manager-ref="accessDecisionManager">
|
||||||
|
<anonymous enabled="false" />
|
||||||
|
<intercept-url pattern="/api//v1/**" method="OPTIONS"
|
||||||
|
access="permitAll" requires-channel="https" />
|
||||||
|
<intercept-url pattern="/api/v1/**" access="isFullyAuthenticated()"
|
||||||
|
method="GET" requires-channel="https" />
|
||||||
|
<intercept-url pattern="/api/v1/**" access="isFullyAuthenticated()"
|
||||||
|
method="POST" requires-channel="https" />
|
||||||
|
<intercept-url pattern="/api/v1/**" access="isFullyAuthenticated()"
|
||||||
|
method="PUT" requires-channel="https" />
|
||||||
|
<intercept-url pattern="/api/v1/**" access="isFullyAuthenticated()"
|
||||||
|
method="DELETE" requires-channel="https" />
|
||||||
|
<intercept-url pattern="/api/v1/**" access="isFullyAuthenticated()"
|
||||||
|
method="HEAD" requires-channel="https" />
|
||||||
|
<custom-filter ref="tenantIdentifierProcessingFilter"
|
||||||
|
position="FIRST" />
|
||||||
|
<custom-filter before="PRE_AUTH_FILTER" ref="resourceServerFilter" />
|
||||||
|
<access-denied-handler ref="oauthAccessDeniedHandler" />
|
||||||
|
</http>
|
||||||
|
|
||||||
|
<http pattern="/api/oauth/token" create-session="stateless"
|
||||||
|
entry-point-ref="oauthAuthenticationEntryPoint" use-expressions="true"
|
||||||
|
authentication-manager-ref="clientAuthenticationManager">
|
||||||
|
<intercept-url pattern="/api/oauth/token" method="OPTIONS"
|
||||||
|
access="permitAll" requires-channel="https" />
|
||||||
|
<intercept-url pattern="/api/oauth/token" method="POST"
|
||||||
|
access="isFullyAuthenticated()" requires-channel="https" />
|
||||||
|
<anonymous enabled="false" />
|
||||||
|
|
||||||
|
<custom-filter ref="tenantIdentifierProcessingFilter"
|
||||||
|
position="FIRST" />
|
||||||
|
<custom-filter ref="clientCredentialsTokenEndpointFilter"
|
||||||
|
before="BASIC_AUTH_FILTER" />
|
||||||
|
<access-denied-handler ref="oauthAccessDeniedHandler" />
|
||||||
|
</http>
|
||||||
|
<beans:bean id="oauthAuthenticationEntryPoint"
|
||||||
|
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
|
||||||
|
<beans:property name="realmName" value="Mifos Platform API" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="oauthAccessDeniedHandler"
|
||||||
|
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
|
||||||
|
|
||||||
|
|
||||||
|
<beans:bean id="clientCredentialsTokenEndpointFilter"
|
||||||
|
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
|
||||||
|
<beans:constructor-arg value="/api/oauth/token" />
|
||||||
|
<beans:property name="authenticationManager" ref="clientAuthenticationManager" />
|
||||||
|
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="accessDecisionManager"
|
||||||
|
class="org.springframework.security.access.vote.UnanimousBased">
|
||||||
|
<beans:constructor-arg>
|
||||||
|
<beans:list>
|
||||||
|
<beans:bean
|
||||||
|
class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
|
||||||
|
<beans:bean class="org.springframework.security.access.vote.RoleVoter" />
|
||||||
|
<beans:bean
|
||||||
|
class="org.springframework.security.access.vote.AuthenticatedVoter" />
|
||||||
|
<beans:bean
|
||||||
|
class="org.springframework.security.web.access.expression.WebExpressionVoter" />
|
||||||
|
</beans:list>
|
||||||
|
</beans:constructor-arg>
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
|
||||||
|
<authentication-manager id="clientAuthenticationManager">
|
||||||
|
<authentication-provider user-service-ref="clientDetailsUserService" />
|
||||||
|
</authentication-manager>
|
||||||
|
|
||||||
|
<authentication-manager alias="authenticationManager"
|
||||||
|
erase-credentials="false">
|
||||||
|
<authentication-provider ref="customAuthenticationProvider" />
|
||||||
|
</authentication-manager>
|
||||||
|
|
||||||
|
<beans:bean id="clientDetailsUserService"
|
||||||
|
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
|
||||||
|
<beans:constructor-arg ref="clientDetailsService" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
|
||||||
|
<beans:bean id="passwordEncoder"
|
||||||
|
class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
|
||||||
|
<beans:constructor-arg value="256" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="saltSource"
|
||||||
|
class="org.springframework.security.authentication.dao.ReflectionSaltSource">
|
||||||
|
<beans:property name="userPropertyToUse" value="id" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="customAuthenticationProvider"
|
||||||
|
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
||||||
|
<beans:property name="userDetailsService" ref="userDetailsService" />
|
||||||
|
<beans:property name="passwordEncoder" ref="passwordEncoder" />
|
||||||
|
<beans:property name="saltSource" ref="saltSource" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<beans:bean id="clientDetailsService"
|
||||||
|
class="org.springframework.security.oauth2.provider.client.JdbcClientDetailsService">
|
||||||
|
<beans:constructor-arg ref="routingDataSource" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
|
||||||
|
<beans:bean id="tokenStore"
|
||||||
|
class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
|
||||||
|
<beans:constructor-arg ref="routingDataSource" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="tokenServices"
|
||||||
|
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
|
||||||
|
<beans:property name="tokenStore" ref="tokenStore" />
|
||||||
|
<beans:property name="clientDetailsService" ref="clientDetailsService" />
|
||||||
|
<beans:property name="supportRefreshToken" value="true" />
|
||||||
|
<beans:property name="refreshTokenValiditySeconds"
|
||||||
|
value="86400" />
|
||||||
|
<beans:property name="accessTokenValiditySeconds"
|
||||||
|
value="3600" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="userApprovalHandler"
|
||||||
|
class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
|
||||||
|
<beans:property name="tokenStore" ref="tokenStore" />
|
||||||
|
<beans:property name="clientDetailsService" ref="clientDetailsService" />
|
||||||
|
<beans:property name="requestFactory" ref="oAuth2RequestFactory" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="oAuth2RequestFactory"
|
||||||
|
class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
|
||||||
|
<beans:constructor-arg ref="clientDetailsService" />
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<oauth:authorization-server
|
||||||
|
client-details-service-ref="clientDetailsService" token-services-ref="tokenServices"
|
||||||
|
user-approval-handler-ref="userApprovalHandler" token-endpoint-url="/api/oauth/token"
|
||||||
|
authorization-endpoint-url="/api/oauth/authorize">
|
||||||
|
<oauth:refresh-token />
|
||||||
|
<oauth:password />
|
||||||
|
</oauth:authorization-server>
|
||||||
|
|
||||||
|
<oauth:resource-server id="resourceServerFilter"
|
||||||
|
token-services-ref="tokenServices" />
|
||||||
|
</beans:beans>
|
||||||
|
</beans:beans>
|
||||||
|
|
||||||
<custom-filter after="SECURITY_CONTEXT_FILTER"
|
|
||||||
ref="basicAuthenticationProcessingFilter" />
|
|
||||||
</http>
|
|
||||||
|
|
||||||
<beans:bean id="basicAuthenticationEntryPoint"
|
|
||||||
class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
|
|
||||||
<beans:property name="realmName" value="Mifos Platform API" />
|
|
||||||
</beans:bean>
|
|
||||||
|
|
||||||
<beans:bean id="passwordEncoder"
|
|
||||||
class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
|
|
||||||
<beans:constructor-arg value="256" />
|
|
||||||
</beans:bean>
|
|
||||||
|
|
||||||
<beans:bean id="saltSource"
|
|
||||||
class="org.springframework.security.authentication.dao.ReflectionSaltSource">
|
|
||||||
<beans:property name="userPropertyToUse" value="id" />
|
|
||||||
</beans:bean>
|
|
||||||
|
|
||||||
<beans:bean id="customAuthenticationProvider"
|
|
||||||
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
|
||||||
<beans:property name="userDetailsService" ref="userDetailsService" />
|
|
||||||
<beans:property name="passwordEncoder" ref="passwordEncoder" />
|
|
||||||
<beans:property name="saltSource" ref="saltSource" />
|
|
||||||
</beans:bean>
|
|
||||||
|
|
||||||
<authentication-manager alias="authenticationManager"
|
|
||||||
erase-credentials="false">
|
|
||||||
<authentication-provider ref="customAuthenticationProvider" />
|
|
||||||
</authentication-manager>
|
|
||||||
</beans:beans>
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
#
|
||||||
|
|
||||||
|
spring.profiles.default=basicauth
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
CREATE TABLE `oauth_client_details` (
|
||||||
|
`client_id` varchar(128) NOT NULL,
|
||||||
|
`resource_ids` varchar(256) DEFAULT NULL,
|
||||||
|
`client_secret` varchar(256) DEFAULT NULL,
|
||||||
|
`scope` varchar(256) DEFAULT NULL,
|
||||||
|
`authorized_grant_types` varchar(256) DEFAULT NULL,
|
||||||
|
`web_server_redirect_uri` varchar(256) DEFAULT NULL,
|
||||||
|
`authorities` varchar(256) DEFAULT NULL,
|
||||||
|
`access_token_validity` int(11) DEFAULT NULL,
|
||||||
|
`refresh_token_validity` int(11) DEFAULT NULL,
|
||||||
|
`additional_information` varchar(4096) DEFAULT NULL,
|
||||||
|
`autoapprove` BIT(1) NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`client_id`)
|
||||||
|
)
|
||||||
|
COLLATE='utf8_general_ci'
|
||||||
|
ENGINE=InnoDB
|
||||||
|
AUTO_INCREMENT=1;
|
||||||
|
|
||||||
|
INSERT INTO `oauth_client_details` (`client_id`, `client_secret`, `scope`, `authorized_grant_types`) VALUES ('community-app', '123', 'all', 'password,refresh_token');
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE `oauth_access_token` (
|
||||||
|
`token_id` varchar(256) DEFAULT NULL,
|
||||||
|
`token` blob,
|
||||||
|
`authentication_id` varchar(256) DEFAULT NULL,
|
||||||
|
`user_name` varchar(256) DEFAULT NULL,
|
||||||
|
`client_id` varchar(256) DEFAULT NULL,
|
||||||
|
`authentication` blob,
|
||||||
|
`refresh_token` varchar(256) DEFAULT NULL
|
||||||
|
)
|
||||||
|
COLLATE='utf8_general_ci'
|
||||||
|
ENGINE=InnoDB
|
||||||
|
AUTO_INCREMENT=1;
|
||||||
|
|
||||||
|
CREATE TABLE `oauth_refresh_token` (
|
||||||
|
`token_id` varchar(256) DEFAULT NULL,
|
||||||
|
`token` blob,
|
||||||
|
`authentication` blob
|
||||||
|
)
|
||||||
|
COLLATE='utf8_general_ci'
|
||||||
|
ENGINE=InnoDB;
|
||||||
|
|
||||||
+3
@@ -14,12 +14,15 @@ import org.mifosplatform.infrastructure.configuration.spring.TestsWithoutDatabas
|
|||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.test.context.web.WebAppConfiguration;
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
|
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@WebAppConfiguration
|
@WebAppConfiguration
|
||||||
|
@ActiveProfiles("basicauth")
|
||||||
@ContextConfiguration(classes = TestsWithoutDatabaseAndNoJobsConfiguration.class)
|
@ContextConfiguration(classes = TestsWithoutDatabaseAndNoJobsConfiguration.class)
|
||||||
public class CommandHandlerProviderTest {
|
public class CommandHandlerProviderTest {
|
||||||
|
|
||||||
|
|||||||
+2
@@ -7,6 +7,7 @@ package org.mifosplatform.infrastructure.configuration.spring;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.test.context.web.WebAppConfiguration;
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
@@ -25,6 +26,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
|||||||
*/
|
*/
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@WebAppConfiguration
|
@WebAppConfiguration
|
||||||
|
@ActiveProfiles("basicauth")
|
||||||
@ContextConfiguration(classes = TestsWithoutDatabaseAndNoJobsConfiguration.class)
|
@ContextConfiguration(classes = TestsWithoutDatabaseAndNoJobsConfiguration.class)
|
||||||
public class SpringConfigurationTest {
|
public class SpringConfigurationTest {
|
||||||
|
|
||||||
|
|||||||
+2
@@ -9,12 +9,14 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mifosplatform.ServerWithMariaDB4jApplication;
|
import org.mifosplatform.ServerWithMariaDB4jApplication;
|
||||||
import org.springframework.boot.test.IntegrationTest;
|
import org.springframework.boot.test.IntegrationTest;
|
||||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.test.context.web.WebAppConfiguration;
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
|
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@SpringApplicationConfiguration(classes = ServerWithMariaDB4jApplication.Configuration.class)
|
@SpringApplicationConfiguration(classes = ServerWithMariaDB4jApplication.Configuration.class)
|
||||||
@WebAppConfiguration
|
@WebAppConfiguration
|
||||||
|
@ActiveProfiles("basicauth")
|
||||||
@IntegrationTest({ "server.port=0", "management.port=0", "mariaDB4j.port=0", "mariaDB4j.dataDir=null" })
|
@IntegrationTest({ "server.port=0", "management.port=0", "mariaDB4j.port=0", "mariaDB4j.dataDir=null" })
|
||||||
public abstract class AbstractSpringBootWithMariaDB4jIntegrationTest {
|
public abstract class AbstractSpringBootWithMariaDB4jIntegrationTest {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user