Skip to content
This repository has been archived by the owner on Dec 5, 2024. It is now read-only.

Commit

Permalink
Add secret resolver factory methods to secrets extension (#300)
Browse files Browse the repository at this point in the history
Description
===========

This patch adds factory methods for the internal secret resolver
classes. This allows to create these resolvers without exporting
the internal classes and their constructors etc to the public API.

The provided API for this is behind a traid `SecretResolverFactory`

I also added two helper methods to create AwsCredentialsProvider objects.

```groovy
SecretResolver awsSecretResolver(AwsCredentialsProvider credentials, Region region)
SecretResolver awsSecretResolver(String profileName, Region region)
SecretResolver awsSecretResolver(Region region)
SecretResolver environmentResolver()
SecretResolver chainResolver(SecretResolver... resolvers)
SecretResolver chainResolver(Iterable<SecretResolver> resolvers)
AwsCredentialsProvider awsCredentialsProvider(String profileName)
AwsCredentialsProvider awsCredentialsProvider(String profileName, ProfileFile profileFile)
```

Changes
=======

* ![ADD] `SecretResolverFactory` methods to secrets extension
  • Loading branch information
Larusso authored Nov 4, 2021
1 parent 0b00cb0 commit 694290a
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,6 @@ class SecretsPluginIntegrationSpec extends SecretsIntegrationSpec {
}))
}
""".stripIndent()


}

}
59 changes: 59 additions & 0 deletions src/main/groovy/wooga/gradle/secrets/SecretResolverFactory.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2021 Wooga GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package wooga.gradle.secrets

import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider
import software.amazon.awssdk.profiles.ProfileFile
import software.amazon.awssdk.regions.Region
import wooga.gradle.secrets.internal.AWSSecretsManagerResolver
import wooga.gradle.secrets.internal.EnvironmentResolver
import wooga.gradle.secrets.internal.SecretResolverChain

trait SecretResolverFactory {
SecretResolver awsSecretResolver(AwsCredentialsProvider credentials, Region region) {
new AWSSecretsManagerResolver(credentials, region)
}

SecretResolver awsSecretResolver(String profileName, Region region) {
new AWSSecretsManagerResolver(awsCredentialsProvider(profileName), region)
}

SecretResolver awsSecretResolver(Region region) {
new AWSSecretsManagerResolver(region)
}

SecretResolver environmentResolver() {
new EnvironmentResolver()
}

SecretResolver chainResolver(SecretResolver... resolvers) {
chainResolver(resolvers.toList())
}

SecretResolver chainResolver(Iterable<SecretResolver> resolvers) {
new SecretResolverChain(resolvers)
}

AwsCredentialsProvider awsCredentialsProvider(String profileName) {
awsCredentialsProvider(profileName, ProfileFile.defaultProfileFile())
}

AwsCredentialsProvider awsCredentialsProvider(String profileName, ProfileFile profileFile) {
DefaultCredentialsProvider.builder().profileFile(profileFile).profileName(profileName).build()
}
}
1 change: 0 additions & 1 deletion src/main/groovy/wooga/gradle/secrets/SecretsPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import org.apache.commons.lang3.RandomStringUtils
import org.gradle.api.Plugin
import org.gradle.api.Project
import wooga.gradle.secrets.internal.DefaultSecretsPluginExtension
import wooga.gradle.secrets.internal.EnvironmentResolver
import wooga.gradle.secrets.tasks.SecretsTask

import javax.crypto.SecretKeyFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import wooga.gradle.secrets.internal.SecretResolverChain

import java.util.logging.Logger

trait SecretsPluginExtension extends SecretSpec {
trait SecretsPluginExtension implements SecretSpec, SecretResolverFactory {

static Logger LOGGER = Logger.getLogger(SecretsPluginExtension.class.getName());

Expand Down Expand Up @@ -91,4 +91,5 @@ trait SecretsPluginExtension extends SecretSpec {
tempFile
}))
}

}
147 changes: 147 additions & 0 deletions src/test/groovy/wooga/gradle/secrets/SecretsPluginExtensionSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
package wooga.gradle.secrets

import nebula.test.ProjectSpec
import org.junit.Rule
import org.junit.contrib.java.lang.system.EnvironmentVariables
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider
import software.amazon.awssdk.profiles.ProfileFile
import software.amazon.awssdk.regions.Region
import wooga.gradle.secrets.internal.DefaultSecret

class SecretsPluginExtensionSpec extends ProjectSpec {
Expand All @@ -25,6 +30,9 @@ class SecretsPluginExtensionSpec extends ProjectSpec {
SecretsPluginExtension subjectUnderTest
def resolver = Mock(SecretResolver)

@Rule
EnvironmentVariables environmentVariables

def setup() {
project.plugins.apply(PLUGIN_NAME)
subjectUnderTest = project.extensions.findByName('secrets') as SecretsPluginExtension
Expand Down Expand Up @@ -156,4 +164,143 @@ class SecretsPluginExtensionSpec extends ProjectSpec {
noExceptionThrown()
!present
}

def "security resolver factory method awsSecretResolver with region creates resolver"() {
when:
def resolver = subjectUnderTest.awsSecretResolver(Region.US_EAST_1)

then:
resolver != null
SecretResolver.class.isAssignableFrom(resolver.class)
}

def "security resolver factory method awsSecretResolver with profileName and region creates resolver"() {
when:
def resolver = subjectUnderTest.awsSecretResolver("some_profile", Region.US_EAST_1)

then:
resolver != null
SecretResolver.class.isAssignableFrom(resolver.class)
}

def "security resolver factory method awsSecretResolver with credentials provider and region creates resolver"() {
when:
def resolver = subjectUnderTest.awsSecretResolver(DefaultCredentialsProvider.builder().build(), Region.US_EAST_1)

then:
resolver != null
SecretResolver.class.isAssignableFrom(resolver.class)
}

def "security resolver factory method environmentResolver creates resolver"() {
when:
def resolver = subjectUnderTest.environmentResolver()

then:
resolver != null
SecretResolver.class.isAssignableFrom(resolver.class)

}

def "security resolver factory method chainResolver creates resolver"() {
when:
def resolver = subjectUnderTest.chainResolver()

then:
resolver != null
SecretResolver.class.isAssignableFrom(resolver.class)

}

def "security resolver factory method chainResolver with provided resolvers creates resolver"() {
when:
def resolver = subjectUnderTest.chainResolver(subjectUnderTest.environmentResolver(), subjectUnderTest.awsSecretResolver(Region.CN_NORTH_1))

then:
resolver != null
SecretResolver.class.isAssignableFrom(resolver.class)
}

def "method awsCredentialsProvider creates AwsCredentialsProvider with profileName and profileFile"() {
given: "a custom aws profile file"
def credentialsFile = File.createTempFile("some", "awsprofile")
credentialsFile << """
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
[${awsProfileName}]
aws_access_key_id = ${accessKeyId}
aws_secret_access_key = ${awsSecretAccessKey}
""".stripIndent()

def p = ProfileFile.builder().content(credentialsFile.toPath()).type(ProfileFile.Type.CREDENTIALS).build()
def profile = ProfileFile.aggregator().addFile(p).build()

and: "a clean aws environment"
environmentVariables.clear(
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
"AWS_SESSION_TOKEN",
"AWS_CA_Bundle",
"AWS_CONFIG_FILE",
"AWS_SHARED_CREDENTIALS_FILE")

when:
def credentials = subjectUnderTest.awsCredentialsProvider(awsProfileName, profile)

then:
credentials != null
def c = credentials.resolveCredentials()
c.accessKeyId() == accessKeyId
c.secretAccessKey() == awsSecretAccessKey

where:
accessKeyId = 'AKIAIOSFODNN8EXAMPLE'
awsSecretAccessKey = 'wJalrXUtnFEMI/K7MDENG/bPyRfiCYEXAMPLEKEY'
awsProfileName = 'someProfile'
}

def "method awsCredentialsProvider creates AwsCredentialsProvider with profileName"() {
given: "a custom credentials file"
def credentialsFile = File.createTempFile("some", "awsprofile")
credentialsFile << """
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
[${awsProfileName}]
aws_access_key_id = ${accessKeyId}
aws_secret_access_key = ${awsSecretAccessKey}
""".stripIndent()

and: "a clean aws environment"
environmentVariables.clear(
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
"AWS_SESSION_TOKEN",
"AWS_CA_Bundle",
"AWS_CONFIG_FILE",
"AWS_SHARED_CREDENTIALS_FILE")

and: "a new environment value to point to our custom credentials file"
environmentVariables.set("AWS_SHARED_CREDENTIALS_FILE", credentialsFile.absolutePath)

when:
def credentials = subjectUnderTest.awsCredentialsProvider(awsProfileName)

then:
credentials != null
def c = credentials.resolveCredentials()
c.accessKeyId() == accessKeyId
c.secretAccessKey() == awsSecretAccessKey

where:
accessKeyId = 'AKIAIOSFODNN8EXAMPLE'
awsSecretAccessKey = 'wJalrXUtnFEMI/K7MDENG/bPyRfiCYEXAMPLEKEY'
awsProfileName = 'someProfile'
}

}

0 comments on commit 694290a

Please sign in to comment.