Skip to content

Commit

Permalink
Try #265:
Browse files Browse the repository at this point in the history
  • Loading branch information
bors[bot] authored Mar 29, 2021
2 parents 68d2955 + f1558ea commit 2f2efb7
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 35 deletions.
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ julia = "1"

[extras]
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[targets]
test = ["Pkg", "Suppressor", "Test", "UUIDs"]
test = ["Pkg", "Random", "Suppressor", "Test", "UUIDs"]
49 changes: 36 additions & 13 deletions src/AWSCredentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -463,40 +463,44 @@ function dot_aws_config(profile=nothing)
end


# https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html#cli-configure-role-oidc
function credentials_from_webtoken()
token_role_arn = "AWS_ROLE_ARN"
token_role_session = "AWS_ROLE_SESSION_NAME"
token_web_identity = "AWS_WEB_IDENTITY_TOKEN_FILE"
token_web_identity_file = "AWS_WEB_IDENTITY_TOKEN_FILE"
token_role_session = "AWS_ROLE_SESSION_NAME" # Optional session name

has_all_keys =
haskey(ENV, token_role_arn) &&
haskey(ENV, token_role_session) &&
haskey(ENV, token_web_identity)

if !has_all_keys
if !(haskey(ENV, token_role_arn) && haskey(ENV, token_web_identity_file))
return nothing
end

role_arn = ENV[token_role_arn]
role_session = ENV[token_role_session]
web_identity = read(ENV["AWS_WEB_IDENTITY_TOKEN_FILE"], String)
role_session = get(ENV, token_role_session) do
_role_session_name(
"AWS.jl-role-",
basename(role_arn),
"-" * Dates.format(@mock(now(UTC)), dateformat"yyyymmdd\THHMMSS\Z"),
)
end

resp = @mock AWSServices.sts(
resp = AWSServices.sts(
"AssumeRoleWithWebIdentity",
Dict(
"RoleArn" => role_arn,
"RoleSessionName" => role_session,
"WebIdentityToken" => web_identity
"RoleSessionName" => role_session, # Required by AssumeRoleWithWebIdentity
"WebIdentityToken" => web_identity,
);
aws_config=AWSConfig(creds=nothing)
)

role_creds = resp["AssumeRoleWithWebIdentityResult"]["Credentials"]
assumed_role_user = resp["AssumeRoleWithWebIdentityResult"]["AssumedRoleUser"]

return AWSCredentials(
role_creds["AccessKeyId"],
role_creds["SecretAccessKey"],
role_creds["SessionToken"];
role_creds["SessionToken"],
assumed_role_user["Arn"];
expiry=DateTime(rstrip(role_creds["Expiration"], 'Z')),
renew=credentials_from_webtoken
)
Expand Down Expand Up @@ -625,3 +629,22 @@ function _get_ini_value(

return value
end


"""
_role_session_name(
prefix::AbstractString,
name::AbstractString,
suffix::AbstractString,
) -> String
Generate a valid role session name. Currently only ensures that the session name is
64-characters or less.
"""
function _role_session_name(prefix, name, suffix)
b = IOBuffer()
write(b, prefix, name)
truncate(b, min(64 - length(suffix), b.size)) # Assumes ASCII
write(b, suffix)
return String(take!(b))
end
53 changes: 45 additions & 8 deletions test/AWSCredentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ end
)
end

@testset "_role_session_name" begin
@test AWS._role_session_name("prefix-", "name", "-suffix") == "prefix-name-suffix"
@test AWS._role_session_name("a" ^ 22, "b" ^ 22, "c" ^ 22) == "a" ^ 22 * "b" ^ 20 * "c" ^ 22
end

@testset "AWSCredentials" begin
@testset "Defaults" begin
creds = AWSCredentials("access_key_id" ,"secret_key")
Expand Down Expand Up @@ -468,30 +473,62 @@ end
mktempdir() do dir
web_identity_file = joinpath(dir, "web_identity")
write(web_identity_file, "foobar")
session_name = "foobar-session"

access_key = "access-key-$(randstring(6))"
secret_key = "secret-key-$(randstring(6))"
session_token = "session-token-$(randstring(6))"
role_arn = "arn:aws:sts::1234:assumed-role/foobar"

patch = Patches._web_identity_patch(;
access_key=access_key,
secret_key=secret_key,
session_token=session_token,
role_arn=role_arn,
)

withenv(
"AWS_ROLE_ARN" => "foobar",
"AWS_ROLE_SESSION_NAME" => Patches.web_sesh_token,
"AWS_WEB_IDENTITY_TOKEN_FILE" => web_identity_file,
"AWS_ROLE_SESSION_NAME" => session_name,
) do
apply(Patches._web_identity_patch) do
apply(patch) do
result = credentials_from_webtoken()

@test result.access_key_id == Patches.web_access_key
@test result.secret_key == Patches.web_secret_key
@test result.token == Patches.web_sesh_token
@test result.access_key_id == access_key
@test result.secret_key == secret_key
@test result.token == session_token
@test result.user_arn == role_arn * "/" * session_name
@test result.renew == credentials_from_webtoken
expiry = result.expiry

result = check_credentials(result)

@test result.access_key_id == Patches.web_access_key
@test result.secret_key == Patches.web_secret_key
@test result.token == Patches.web_sesh_token
@test result.access_key_id == access_key
@test result.secret_key == secret_key
@test result.token == session_token
@test result.user_arn == role_arn * "/" * session_name
@test result.renew == credentials_from_webtoken
@test expiry != result.expiry
end
end

session_name = "AWS.jl-role-foobar-20210101T000000Z"
patches = [
patch
@patch Dates.now(::Type{UTC}) = DateTime(2021)
]

withenv(
"AWS_ROLE_ARN" => "foobar",
"AWS_WEB_IDENTITY_TOKEN_FILE" => web_identity_file,
"AWS_ROLE_SESSION_NAME" => nothing,
) do
apply(patches) do
result = credentials_from_webtoken()
@test result.user_arn == role_arn * "/" * session_name
end
end
end
end

Expand Down
37 changes: 24 additions & 13 deletions test/patch.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ body = """

response = HTTP.Messages.Response()

web_access_key = "web_identity_access_key"
web_secret_key = "web_identity_secret_key"
web_sesh_token = "web_session_token"

function _response!(; version::VersionNumber=version, status::Int64=status, headers::Array=headers, body::String=body)
response.version = version
response.status = status
Expand All @@ -69,17 +65,32 @@ _config_file_patch = @patch function dot_aws_config_file()
return ""
end

_web_identity_patch = @patch function AWS._http_request(request)
creds = Dict(
"AccessKeyId" => web_access_key,
"SecretAccessKey" => web_secret_key,
"SessionToken" => web_sesh_token,
"Expiration" => string(now(UTC))
)
_web_identity_patch = function (;
access_key="web_identity_access_key",
secret_key="web_identity_secret_key",
session_token="web_session_token",
role_arn="arn:aws:sts:::assumed-role/role-name",
)
@patch function AWS._http_request(request)
params = Dict(split.(split(request.content, '&'), '='))
creds = Dict(
"AccessKeyId" => access_key,
"SecretAccessKey" => secret_key,
"SessionToken" => session_token,
"Expiration" => string(now(UTC)),
)

result = Dict("AssumeRoleWithWebIdentityResult" => Dict("Credentials" => creds))
result = Dict(
"AssumeRoleWithWebIdentityResult" => Dict(
"Credentials" => creds,
"AssumedRoleUser" => Dict(
"Arn" => role_arn * "/" * params["RoleSessionName"],
),
),
)

return HTTP.Response(200, ["Content-Type" => "text/json", "charset" => "utf-8"], body=json(result))
return HTTP.Response(200, ["Content-Type" => "text/json", "charset" => "utf-8"], body=json(result))
end
end

_github_tree_patch = @patch function tree(repo, tree_obj; kwargs...)
Expand Down
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ using OrderedCollections: LittleDict, OrderedDict
using MbedTLS: digest, MD_SHA256, MD_MD5
using Mocking
using Pkg
using Random
using Retry
using Suppressor
using Test
Expand Down

0 comments on commit 2f2efb7

Please sign in to comment.