Skip to content

Commit

Permalink
Merge pull request #287 from wultra/develop
Browse files Browse the repository at this point in the history
Merge develop into master
  • Loading branch information
romanstrobl authored Jun 10, 2021
2 parents a4c5f0f + 6f72d89 commit 175eade
Show file tree
Hide file tree
Showing 94 changed files with 845 additions and 5,559 deletions.
67 changes: 67 additions & 0 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
push:
branches: [ develop, master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ develop ]
schedule:
- cron: '27 4 * * 2'

jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed

steps:
- name: Checkout repository
uses: actions/checkout@v2

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl

# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language

#- run: |
# make bootstrap
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
148 changes: 64 additions & 84 deletions docs/RESTful-API-for-Spring.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ This step is technically required only in case your server uses end-to-end encry
```xml
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk15on</artifactId>
<artifactId>bcprov-jdk15on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
```
Expand Down Expand Up @@ -94,6 +94,7 @@ public PowerAuthClient powerAuthClient() {
## Advanced PowerAuth REST Client Configuration

The following REST client options are available:

- `maxMemorySize` - configures maximum memory size per request, default 1 MB
- `connectTimeout` - configures connection timeout, default 5000 ms
- `proxyEnabled` - enables proxy, disabled by default
Expand Down Expand Up @@ -156,13 +157,13 @@ public class WebApplicationConfig implements WebMvcConfigurer {

`PowerAuthInterceptor` bean is responsible for the `@PowerAuth` annotation handling (see example in [Verify Signatures Chapter](#verify-signatures)). You need to add it to the interceptor registry.

And finally, the `FilterRegistrationBean` (with the `PowerAuthRequestFilter` filter) is a technical component that passes the HTTP request body as an attribute of `HttpServletRequest`, so that it can be used for signature validation.
Finally, the `FilterRegistrationBean` (with the `PowerAuthRequestFilter` filter) is a technical component that passes the HTTP request body as an attribute of `HttpServletRequest`, so that it can be used for signature validation.

### Register a PowerAuth Application Configuration

_(optional)_

PowerAuth uses the concept of `application ID` and `application secret`. While `applicationId` attribute is transmitted with requests in `X-PowerAuth-Authorization` header, `applicationSecret` is shared implicitly between client and server and is a part of the actual signature value. Applications are a first class citizen in PowerAuth protocol. Intermediate application, however, may influence which applications are accepted by implementing following configuration.
PowerAuth uses the concept of `application ID` and `application secret`. While `applicationId` attribute is transmitted with requests in `X-PowerAuth-Authorization` header, `applicationSecret` is shared implicitly between the client and server and is a part of the actual signature value. Applications are a first class citizen in PowerAuth protocol. Intermediate application, however, may influence which applications are accepted by implementing following configuration.

```java
@Configuration
Expand Down Expand Up @@ -211,7 +212,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {

This sample `@Controller` implementation illustrates how to use `@PowerAuth` annotation to verify that the request signature matches what is expected - in this case, to establish an authenticated session. In case the authentication is not successful, the `PowerAuthApiAuthentication` object is `null`. You may check for the `null` value and raise `PowerAuthAuthenticationException` that is handled alongside other application exceptions via default `@ControllerAdvice`.

_Note: Controllers that establish a session must not be on a context that is protected by Spring Security (for example "/secured/", in our example), otherwise context could never be reached and session will never be established._
<!-- begin box info -->
Note: Controllers that establish a session must not be on a context that is protected by Spring Security (for example `/secured/`, in our example), otherwise context could never be reached and session will never be established.
<!-- end -->

```java
@Controller
Expand All @@ -227,7 +230,7 @@ public class AuthenticationController {
throw new PowerAuthSignatureInvalidException();
}
// use userId if needed ...
String userId = auth.getUserId();
final String userId = auth.getUserId();

// create authenticated session
SecurityContextHolder.getContext().setAuthentication((Authentication) auth);
Expand All @@ -239,6 +242,41 @@ public class AuthenticationController {
}
```

The `resourceId` parameter of the `@PowerAuth` annotation can substitute placeholders (marked via "${placeholder}") with the actual parameters of the handler method. Mobile client can construct resource ID values in a dynamic way accordingly. The implementation takes into account all handler method parameters that are annotated via `@RequestParam` or `@PathVariable` annotations and extracts values from the request parameter map.

<!-- begin box info -->
In case both `@RequestParam` and `@PathVariable` with the same name exist, the value of `@RequestParam` takes precedence. This is because `@RequestParam` usually maps to the HTTP GET query parameter that cannot be easily changed in existing API, while `@PathVariable` is just a URL placeholder that can be renamed in the code with no impact on functionality.
<!-- end -->

Example of using dynamic resource ID:

```java
@Controller
@RequestMapping(value = "secured")
public class AuthenticationController {

@RequestMapping(value = "account/{id}", method = RequestMethod.POST)
@PowerAuth(resourceId = "/secured/account/${id}?filter=${filter}")
@ResponseBody
public MyAccountApiResponse changeAccountSettings(
@PathVariable("id") String accountId, @RequestParam("filter") String filter, PowerAuthApiAuthentication auth) {

if (auth == null) {
// handle authentication failure
throw new PowerAuthSignatureInvalidException();
}

// use userId for business logic ...
final String userId = auth.getUserId();
final Account account = myService.updateAccount(accountId, userId, filter);

// return OK response
return new MyAccountApiResponse(Status.OK, userId);
}

}
```

In case you need a more low-level access to the signature verification, you can verify the signature manually using the `PowerAuthAuthenticationProvider` like this:

```java
Expand All @@ -250,15 +288,16 @@ public class AuthenticationController {
private PowerAuthAuthenticationProvider authenticationProvider;

@RequestMapping(value = "login", method = RequestMethod.POST)
public @ResponseBody PowerAuthAPIResponse<String> login(
@RequestHeader(value = PowerAuthSignatureHttpHeader.HEADER_NAME, required = true) String signatureHeader,
HttpServletRequest servletRequest) throws Exception {

PowerAuthApiAuthentication apiAuthentication = authenticationProvider.validateRequestSignature(
"POST",
"Any data".getBytes(StandardCharsets.UTF_8),
"/session/login",
signatureHeader
@ResponseBody
public PowerAuthAPIResponse<String> login(
@RequestHeader(value = PowerAuthSignatureHttpHeader.HEADER_NAME, required = true) String signatureHeader,
HttpServletRequest servletRequest) throws Exception {

final PowerAuthApiAuthentication apiAuthentication = authenticationProvider.validateRequestSignature(
"POST",
"Any data".getBytes(StandardCharsets.UTF_8),
"/session/login",
signatureHeader
);

if (apiAuthentication == null || apiAuthentication.getUserId() == null) {
Expand Down Expand Up @@ -302,9 +341,9 @@ public class AuthenticationController {

## Use End-To-End Encryption

You can use end-to-end encryption to add an additional encryption layer on top of the basic HTTPS encryption to protect the request body contents better.
You can use end-to-end encryption to add additional encryption layer on top of the basic HTTPS encryption to protect the request body contents better.

End-to-end encryption provided by PowerAuth uses `POST` method for all data transport and it requires predefined request / response structure.
End-to-end encryption provided by PowerAuth uses `POST` method for all data transport, and it requires a predefined request / response structure.

### Encryption in Application Scope

Expand All @@ -330,9 +369,9 @@ public class EncryptedDataExchangeController {
}
```

The method argument annotated by the `@EncryptedRequestBody` annotation is set with decrypted request data. The data is decrypted using ECIES decryptor initialized in `application` scope.
The method argument annotated by the `@EncryptedRequestBody` annotation is set with decrypted request data. The data is decrypted using an ECIES decryptor initialized in `application` scope.

The response data is automatically encrypted using the previously created ECIES decryptor which was used for decrypting the request data.
The response data is automatically encrypted using the previously created an ECIES decryptor which was used for decrypting the request data.

### Encryption in Activation Scope

Expand All @@ -358,9 +397,9 @@ public class EncryptedDataExchangeController {
}
```

The method argument annotated by the `@EncryptedRequestBody` annotation is set with decrypted request data. The data is decrypted using ECIES decryptor initialized in `activation` scope.
The method argument annotated by the `@EncryptedRequestBody` annotation is set with decrypted request data. The data is decrypted using an ECIES decryptor initialized in `activation` scope.

The response data is automatically encrypted using the previously created ECIES decryptor which was used for decrypting the request data.
The response data is automatically encrypted using the previously created an ECIES decryptor which was used for decrypting the request data.

### Signed and Encrypted Requests

Expand Down Expand Up @@ -393,69 +432,10 @@ public class EncryptedDataExchangeController {
}
```

The method argument annotated by the `@EncryptedRequestBody` annotation is set with decrypted request data. The data is decrypted using ECIES decryptor initialized in `activation` scope. The signature received in PowerAuth HTTP signature header is verified.

The response data is automatically encrypted using the previously created ECIES decryptor which was used for decrypting the request data.

_Note: You can also use `String` or `byte[]` data types instead of using request/response objects for encryption of raw data._

### Non-Personalized End-To-End Encryption (v2 - legacy)
The method argument annotated by the `@EncryptedRequestBody` annotation is set with decrypted request data. The data is decrypted using an ECIES decryptor initialized in `activation` scope. The signature received in PowerAuth HTTP signature header is verified.

To use the legacy non-personalized (application specific) encryption, use following pattern:
The response data is automatically encrypted using the previously created an ECIES decryptor which was used for decrypting the request data.

```java
@RestController
@RequestMapping(value = "encrypted")
public class EncryptedController {

private EncryptorFactory encryptorFactory;

@Autowired
public void setEncryptorFactory(EncryptorFactory encryptorFactory) {
this.encryptorFactory = encryptorFactory;
}


@RequestMapping(value = "hello", method = RequestMethod.POST)
public PowerAuthApiResponse<NonPersonalizedEncryptedPayloadModel> createNewActivation(@RequestBody PowerAuthApiRequest<NonPersonalizedEncryptedPayloadModel> encryptedRequest) throws PowerAuthActivationException {
try {

// Prepare an encryptor
final PowerAuthNonPersonalizedEncryptor encryptor = encryptorFactory.buildNonPersonalizedEncryptor(encryptedRequest);
if (encryptor == null) {
throw new PowerAuthEncryptionException();
}

// Decrypt the request object
OriginalRequest request = encryptor.decrypt(object, OriginalRequest.class);

if (request == null) {
throw new PowerAuthEncryptionException();
}

// ... do your business logic with OriginalRequest instance

// Create original response object
OriginalResponse response = new OriginalResponse();
response.setAttribute1("attribute1");
response.setAttribute2("attribute2");
response.setAttribute3("attribute3");

// Encrypt response object
final PowerAuthApiResponse<NonPersonalizedEncryptedPayloadModel> encryptedResponse = encryptor.encrypt(response);

if (encryptedResponse == null) {
throw new PowerAuthEncryptionException();
}

// Return response
return encryptedResponse;

} catch (IOException ex) {
throw new PowerAuthActivationException();
}

}

}
```
<!-- begin box info -->
Note: You can use `String` or `byte[]` data types instead of using request/response objects for encryption of raw data.
<!-- end -->
22 changes: 9 additions & 13 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

<groupId>io.getlime.security</groupId>
<artifactId>powerauth-restful-integration-parent</artifactId>
<version>1.0.0</version>
<version>1.1.0</version>
<packaging>pom</packaging>

<inceptionYear>2017</inceptionYear>
Expand Down Expand Up @@ -70,10 +70,8 @@
<modules>
<module>powerauth-restful-model</module>
<module>powerauth-restful-security-base</module>
<module>powerauth-restful-security-javaee</module>
<module>powerauth-restful-security-spring</module>
<module>powerauth-restful-security-spring-annotation</module>
<module>powerauth-restful-server-javaee</module>
<module>powerauth-restful-server-spring</module>
</modules>

Expand All @@ -83,17 +81,15 @@
<maven.compiler.target>1.8</maven.compiler.target>
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
<maven-deploy-plugin.version>3.0.0-M1</maven-deploy-plugin.version>
<maven-javadoc-plugin.version>3.2.0</maven-javadoc-plugin.version>
<maven-javadoc-plugin.version>3.3.0</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
<maven-war-plugin.version>3.3.1</maven-war-plugin.version>
<javaee-api.version>7.0</javaee-api.version>
<javax.servlet-api.version>3.1.0</javax.servlet-api.version>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
<guava.version>30.0-jre</guava.version>
<slf4j-api.version>1.7.30</slf4j-api.version>
<jackson-databind.version>2.11.3</jackson-databind.version>
<bcprov.version>1.67</bcprov.version>
<rest-model-base.version>1.2.0</rest-model-base.version>
<javax.servlet-api.version>4.0.1</javax.servlet-api.version>
<spring-boot.version>2.4.5</spring-boot.version>
<commons-text.version>1.9</commons-text.version>
<jackson-databind.version>2.12.3</jackson-databind.version>
<bcprov.version>1.68</bcprov.version>
<rest-model-base.version>1.3.0</rest-model-base.version>
</properties>

<build>
Expand Down Expand Up @@ -188,7 +184,7 @@
<repositories>
<repository>
<id>ossrh-snapshots</id>
<url>http://oss.sonatype.org/content/repositories/snapshots/</url>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
Expand Down
4 changes: 2 additions & 2 deletions powerauth-restful-model/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>powerauth-restful-model</artifactId>
<version>1.0.0</version>
<version>1.1.0</version>
<name>powerauth-restful-model</name>
<description>Model classes PowerAuth Standard RESTful API</description>

<parent>
<groupId>io.getlime.security</groupId>
<artifactId>powerauth-restful-integration-parent</artifactId>
<version>1.0.0</version>
<version>1.1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,19 @@
* @author Roman Strobl, [email protected]
*/
public enum ActivationType {

/**
* Activation via activation code.
*/
CODE,

/**
* Activation via custom credentials.
*/
CUSTOM,

/**
* Activation via recovery code.
*/
RECOVERY
}
Loading

0 comments on commit 175eade

Please sign in to comment.