Skip to content

Commit

Permalink
Merge pull request #10 from curityio/release-v1.0
Browse files Browse the repository at this point in the history
Release v1.0
  • Loading branch information
ju-cu authored Jul 1, 2022
2 parents 9e537c8 + f90c76b commit 254a98d
Show file tree
Hide file tree
Showing 11 changed files with 284 additions and 62 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2017 Curity AB
Copyright 2022 Curity AB

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
42 changes: 11 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,32 @@
This is an example event listener SDK Plugin for the Curity Identity Server. The plugin registers an event listener
listening for issued access token events, and forwards them to an AWS deployed DynamoDB.

## Changed in 0.1.5
- Update software.amazon.awssdk:bom to 2.17.203 to get netty up on version 4.1.77.

## Changed in 0.1.4
- Update software.amazon.awssdk:bom to 2.17.125 to remove MEDIUM Vulnerability - [GHSA-wx5j-54mm-rqqq](https://github.com/advisories/GHSA-wx5j-54mm-rqqq)

## Changed in 0.1.3
- Added default to hashing algorithm
- Make hashing algorithm configurable

## Changed in 0.1.2
- Optional setting to use an EC2 Instance Profile for DynamoDB access. This allows for assigning an IAM Role with DynamoDB permissions directly to an EC2 instance. The aws-token-publisher will then automatically resolve temporary credentials from that role. If this method is used no Access Key ID, Access Key Secret, Aws Profile Name or Aws Role Arn are needed.
- The AWS Region is now an Enum and is set through a drop-down menu in the configuration instead of a text field.


## Changed in 0.1.1
- Using AWS SDK v2 (2.14.14)
- New SDK version now requires the Region format of `us-west-2` instead of `US_WEST_2`
- Possibility to use credentials from local system (~/.aws/credentials) where plugin is running by specifying a profile name
- Implemented ability to access DynamoDB via AssumeRole when the ARN of the role to assume is specified in configuration
- The `expiration` is now stored as a Number instead of a String so that the [DynamoDB TTL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) function can be leveraged to automatically expire the cached token.

## Building, installation and configuration

To build the plugin, simply download it and run `mvn package`. This creates `identityserver.plugins.events.listeners.aws-token-publisher-0.1.1.jar` and copies all needed dependencies into `target/`
To build the plugin, simply download it and run `mvn package`. This creates `identityserver.plugins.events.listeners.aws-token-publisher-1.0.0.jar` and copies all needed dependencies into `target/`
Create a new folder `aws_token_publisher` in `<idsvr_home>/usr/share/plugins/` then copy all the jar files to that folder
and (re)start the Curity Identity Server. Configure a new event listener (shown here using the Admin UI, but could also be configured through the CLI, REST or XML):

![Add new listener](docs/new-listener.png)

Pick a suitable name and then select the "aws-token-publisher" type:

![Select type](docs/select-type.png)
Pick a suitable name and then select the AWS Token Publisher (`aws-token-publisher`) as type.

Configure your listener by adding:

- AWS Access Key ID and AWS Access Key Secret or
- AWS Profile Name
- AWS Region that the DynamoDB is deployed in (used with Key/Secret to access the DB) and used in order to get temporary credentials using AssumeRole.
- AWS Region that the DynamoDB is deployed in
- Name of the table configured in DynamoDB to hold the split-token information
- Name of the column that is the primary key in the DynamoDB table configured above. This is the column that will store a hash of the token signature
- A DynamoDB Access Method
- AWS Access Key ID and AWS Access Key Secret or
- AWS Profile Name or
- EC2 Instance Profile

If Access Key ID and Access Key Secret are defined in the configuration these will be used instead of loading credentials from the system if `Aws Profile Name` is also defined.
Provide the credentials, that is the **AWS Access Key ID and AWS Access Key Secret**, of the user that has the permission to access the DynamoDB.
Alternatively, choose **AWS Profile Name** to load credentials from the system (i.e. from `~/.aws/credentials`). Provide the name of the profile, that is the name of the entry in the credentials file.

If `Aws Role Arn` is defined an AssumeRole attempt will be made with the credentials found, either from config (Access Key ID and Access Key Secret) or from profile. The credentials then dont have to have direct access to DynamoDB but instead needs to have access to the role that will provide temporary credentials to access DynamoDB.
If **AWS Role Arn** is specified, an AssumeRole attempt will be made with the provided AWS region and the credentials found, either from config (Access Key ID and Access Key Secret) or from profile. The credentials then don't have direct access to DynamoDB but instead need to have access to the role that will provide temporary credentials to access DynamoDB.

Select the option **EC2 Instance Profile** if the Curity Identity Server runs on an EC2 instance and the instance has an IAM role assigned with permissions to access the DynamoDB.

![Configure the listener](docs/configure-listener.png)

Expand Down
Binary file modified docs/configure-listener.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/new-listener.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 44 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!--
~ Copyright 2020 Curity AB
~ Copyright 2022 Curity AB
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
Expand All @@ -21,6 +21,15 @@
<modelVersion>4.0.0</modelVersion>

<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>customer-release-repo</id>
<url>https://nexus.curity.se/nexus/content/repositories/customer-release-repo</url>
Expand All @@ -30,8 +39,10 @@

<groupId>io.curity.identityserver</groupId>
<artifactId>identityserver.plugins.events.listeners.aws-token-publisher</artifactId>
<version>0.1.5</version>
<name>AWS Token Publisher Demo Plugin</name>
<version>1.0.0</version>

<name>AWS Token Publisher Plugin</name>

<licenses>
<license>
<name>Apache-2.0</name>
Expand All @@ -42,6 +53,10 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.identityserver.sdkVersion>7.2.0</project.identityserver.sdkVersion>
<project.awssdkVersion>2.17.220</project.awssdkVersion>
<project.slf4jVersion>1.7.36</project.slf4jVersion>
<project.junitVersion>5.8.2</project.junitVersion>
</properties>

<build>
Expand All @@ -51,8 +66,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<source>17</source>
<target>17</target>
</configuration>
</plugin>

Expand All @@ -65,6 +80,9 @@
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeScope>
compile
</includeScope>
<excludeArtifactIds>
identityserver.sdk
</excludeArtifactIds>
Expand All @@ -81,7 +99,7 @@
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.17.203</version>
<version>${project.awssdkVersion}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand All @@ -92,13 +110,13 @@
<dependency>
<groupId>se.curity.identityserver</groupId>
<artifactId>identityserver.sdk</artifactId>
<version>5.2.0</version>
<version>${project.identityserver.sdkVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.22</version>
<version>${project.slf4jVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand All @@ -109,6 +127,23 @@
<groupId>software.amazon.awssdk</groupId>
<artifactId>sts</artifactId>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${project.junitVersion}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.6.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${project.slf4jVersion}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Curity AB
* Copyright 2022 Curity AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,7 @@
import io.curity.identityserver.plugin.events.listeners.config.AWSEventListenerConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import se.curity.identityserver.sdk.data.events.IssuedAccessTokenOAuthEvent;
import se.curity.identityserver.sdk.errors.ErrorCode;
import se.curity.identityserver.sdk.event.EventListener;
Expand Down Expand Up @@ -86,32 +87,33 @@ public void handle(IssuedAccessTokenOAuthEvent event)
catch (NoSuchAlgorithmException e)
{
_logger.warn("{} must be available in order to use the AWS event listener", _hashingAlgorithm);
throw _exceptionFactory.internalServerException(ErrorCode.GENERIC_ERROR,
"SHA-256 must be available in order to use the AWS event listener");
throw _exceptionFactory.internalServerException(ErrorCode.GENERIC_ERROR,
String.format("%s must be available in order to use the AWS event listener", _hashingAlgorithm)
);
}

digest.update(signature.getBytes());
String hashedSignature = Base64.getEncoder().encodeToString(digest.digest());

/*Use Instance Profile from IAM Role applied to EC2 instance*/
/* Use Instance Profile from IAM Role applied to EC2 instance */
if(_accessMethod.isEC2InstanceProfile().isPresent() && _accessMethod.isEC2InstanceProfile().get()) {
_creds = InstanceProfileCredentialsProvider.builder().build();
}
/* Use AccessKey and Secret from config */
else if(_accessMethod.getAccessKeyIdAndSecret().isPresent()) {
_creds = StaticCredentialsProvider.create(AwsBasicCredentials.create(_accessMethod.getAccessKeyIdAndSecret().get().getAccessKeyId().get(), _accessMethod.getAccessKeyIdAndSecret().get().getAccessKeySecret().get()));
_creds = StaticCredentialsProvider.create(AwsBasicCredentials.create(_accessMethod.getAccessKeyIdAndSecret().get().getAccessKeyId(), _accessMethod.getAccessKeyIdAndSecret().get().getAccessKeySecret()));

/* roleARN is present, get temporary credentials through AssumeRole */
if(_accessMethod.getAccessKeyIdAndSecret().get().getAwsRoleARN().isPresent())
{
_creds = getNewCredentialsFromAssumeRole(_creds, _accessMethod.getAccessKeyIdAndSecret().get().getAwsRoleARN().get());
}
}
/* If a profile name is defined get credentials from configured profile from ~/.aws/credentials */
else if(_accessMethod.getAWSProfile().get().getAwsProfileName().isPresent())
/* If a profile name is defined, get credentials from configured profile from ~/.aws/credentials */
else if(_accessMethod.getAWSProfile().isPresent())
{
_creds = ProfileCredentialsProvider.builder()
.profileName(_accessMethod.getAWSProfile().get().getAwsProfileName().get())
.profileName(_accessMethod.getAWSProfile().get().getAwsProfileName())
.build();

/* roleARN is present, get temporary credentials through AssumeRole */
Expand Down Expand Up @@ -146,7 +148,7 @@ private AwsCredentialsProvider getNewCredentialsFromAssumeRole(AwsCredentialsPro
{
_logger.warn("AssumeRole Request sent but was not successful: {}",
assumeRoleResult.sdkHttpResponse().statusText().get() );
return creds; //Returning the original credentials
return creds; //returning the original credentials
}
else
{
Expand Down Expand Up @@ -198,6 +200,8 @@ private void putTokenData(String hashedSignature,IssuedAccessTokenOAuthEvent eve
}
catch (Exception e)
{
_logger.warn("Failed to post event to AWS DynamoDB.");
_logger.debug("Error while writing to AWS DynamoDB: {}", e.getMessage(), e);
throw _exceptionFactory.internalServerException(ErrorCode.EXTERNAL_SERVICE_ERROR);
}
finally
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Curity AB
* Copyright 2022 Curity AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,7 +18,6 @@

import se.curity.identityserver.sdk.config.Configuration;
import se.curity.identityserver.sdk.config.OneOf;
import se.curity.identityserver.sdk.config.annotation.DefaultBoolean;
import se.curity.identityserver.sdk.config.annotation.DefaultEnum;
import se.curity.identityserver.sdk.config.annotation.DefaultString;
import se.curity.identityserver.sdk.config.annotation.Description;
Expand Down Expand Up @@ -51,9 +50,9 @@ interface AWSAccessMethod extends OneOf

interface AccessKeyIdAndSecret
{
Optional<String> getAccessKeyId();
String getAccessKeyId();

Optional<String> getAccessKeySecret();
String getAccessKeySecret();

@Description("Optional role ARN used when requesting temporary credentials, ex. arn:aws:iam::123456789012:role/dynamodb-role")
Optional<String> getAwsRoleARN();
Expand All @@ -62,22 +61,22 @@ interface AccessKeyIdAndSecret
interface AWSProfile
{
@Description("AWS Profile name. Retrieves credentials from the system (~/.aws/credentials)")
Optional<String> getAwsProfileName();
String getAwsProfileName();

@Description("Optional role ARN used when requesting temporary credentials, ex. arn:aws:iam::123456789012:role/dynamodb-role")
Optional<String> getAwsRoleARN();
}

@Description("EC2 instance that the Curity Identity Server is running on has been assigned an IAM Role with permissions to DynamoDB.")
Optional<@DefaultBoolean(false) Boolean> isEC2InstanceProfile();
Optional<Boolean> isEC2InstanceProfile();
}

ExceptionFactory getExceptionFactory();

@Description("Configure the hashing algorithm that will be used to hash the signature of the split token.")
@DefaultEnum("sha_256")
HashingAlgorithm getHashingAlgorithm();

@DefaultEnum("SHA-256")
enum HashingAlgorithm
{
sha_256("SHA-256"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Curity AB
* Copyright 2022 Curity AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -26,18 +26,20 @@ public enum AWSRegion {
us_west_2("us-west-2"),
af_south_1("af-south-1"),
eu_north_1("eu-north-1"),
eu_west_3("eu-west-3"),
eu_west_2("eu-west-2"),
eu_west_1("eu-west-1"),
ap_northeast_2("ap-northeast-2"),
eu_west_2("eu-west-2"),
eu_west_3("eu-west-3"),
ap_northeast_1("ap-northeast-1"),
ap_northeast_2("ap-northeast-2"),
ap_northeast_3("ap-northeast-3"),
me_south_1("me-south-1"),
sa_east_1("sa-east-1"),
ap_east_1("ap-east-1"),
cn_north_1("cn-north-1"),
us_gov_west_1("us-gov-west-1"),
ap_southeast_1("ap-southeast-1"),
ap_southeast_2("ap-southeast-2"),
ap_southeast_3("ap-southeast-3"),
us_iso_east_1("us-iso-east-1"),
us_east_1("us-east-1"),
us_east_2("us-east-2"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Curity AB
* Copyright 2022 Curity AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Loading

0 comments on commit 254a98d

Please sign in to comment.