diff --git a/secrethub/.credo.exs b/secrethub/.credo.exs index c41516255..1c8f066d0 100644 --- a/secrethub/.credo.exs +++ b/secrethub/.credo.exs @@ -46,7 +46,7 @@ # ## Readability Checks # - {Credo.Check.Readability.AliasOrder, exit_status: 0}, + {Credo.Check.Readability.AliasOrder, false}, {Credo.Check.Readability.FunctionNames}, {Credo.Check.Readability.LargeNumbers}, {Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 120}, diff --git a/secrethub/lib/secrethub/open_id_connect/jwt.ex b/secrethub/lib/secrethub/open_id_connect/jwt.ex index a273e87b1..6f460ff97 100644 --- a/secrethub/lib/secrethub/open_id_connect/jwt.ex +++ b/secrethub/lib/secrethub/open_id_connect/jwt.ex @@ -8,6 +8,8 @@ defmodule Secrethub.OpenIDConnect.JWT do "jti", # Subject of the JWT "sub", + # Compact subject format with comma-separated values only + "sub2", # Recipient for which the JWT is intended "aud", # Issuer of the JWT @@ -114,6 +116,7 @@ defmodule Secrethub.OpenIDConnect.JWT do "branch" => req.git_branch_name, "pr" => req.git_pull_request_number, "sub" => req.subject, + "sub2" => build_compact_subject(req), "iss" => "https://#{req.org_username}.#{domain}", "aud" => "https://#{req.org_username}.#{domain}", "job_type" => req.job_type, @@ -153,4 +156,19 @@ defmodule Secrethub.OpenIDConnect.JWT do Secrethub.OpenIDConnect.JWTFilter.filter_claims(claims, req.org_id, req.project_id) end + + defp build_compact_subject(req) do + [ + req.org_username, + req.project_id, + req.repository_name, + req.git_ref_type, + req.git_ref + ] + |> Enum.map_join(":", &safe_string/1) + end + + defp safe_string(nil), do: "" + defp safe_string(value) when is_binary(value), do: value + defp safe_string(value), do: to_string(value) end diff --git a/secrethub/lib/secrethub/open_id_connect/jwt_claim.ex b/secrethub/lib/secrethub/open_id_connect/jwt_claim.ex index 46a745bba..0db03d3f7 100644 --- a/secrethub/lib/secrethub/open_id_connect/jwt_claim.ex +++ b/secrethub/lib/secrethub/open_id_connect/jwt_claim.ex @@ -113,6 +113,15 @@ defmodule Secrethub.OpenIDConnect.JWTClaim do is_mandatory: false, is_active: true }, + "sub2" => %__MODULE__{ + name: "sub2", + description: + "Compact subject (check sub) format with comma-separated values only (org,project_id,repo,ref_type,ref)", + is_system_claim: true, + is_aws_tag: false, + is_mandatory: false, + is_active: true + }, "org_id" => %__MODULE__{ name: "org_id", description: "Organization ID", diff --git a/secrethub/test/secrethub/internal_grpc_api_test.exs b/secrethub/test/secrethub/internal_grpc_api_test.exs index bc986926f..307703e87 100644 --- a/secrethub/test/secrethub/internal_grpc_api_test.exs +++ b/secrethub/test/secrethub/internal_grpc_api_test.exs @@ -835,6 +835,7 @@ defmodule Secrethub.InternalGrpcApi.Test do assert Map.get(jwt.fields, "aud") == "https://testera.localhost" assert Map.get(jwt.fields, "iss") == "https://testera.localhost" assert Map.get(jwt.fields, "sub") == "project:front:pipeline:semaphore.yml" + assert Map.get(jwt.fields, "sub2") == "testera:#{req.project_id}:::" assert Map.get(jwt.fields, "prj") == req.project_name assert Map.get(jwt.fields, "org") == req.org_username refute Map.has_key?(jwt.fields, "https://aws.amazon.com/tags") @@ -908,6 +909,7 @@ defmodule Secrethub.InternalGrpcApi.Test do assert Map.get(jwt.fields, "aud") == "https://testera.localhost" assert Map.get(jwt.fields, "iss") == "https://testera.localhost" assert Map.get(jwt.fields, "sub") == "project:front:pipeline:semaphore.yml" + assert Map.get(jwt.fields, "sub2") == "testera:#{req.project_id}:my-repo:branch:" end test "it returns a signed token with filtered claims in on_prem mode" do @@ -942,6 +944,7 @@ defmodule Secrethub.InternalGrpcApi.Test do # Essential claims should be present assert Map.get(jwt.fields, "sub") == "project:front:pipeline:semaphore.yml" + assert Map.get(jwt.fields, "sub2") == "testera:#{req.project_id}:my-repo:branch:" assert Map.get(jwt.fields, "aud") == "https://testera.localhost" assert Map.get(jwt.fields, "iss") == "https://testera.localhost" assert_in_delta Map.get(jwt.fields, "exp") + req.expires_in, now, 5 @@ -1022,8 +1025,8 @@ defmodule Secrethub.InternalGrpcApi.Test do } do claim_config = ClaimConfig.new( - name: "sub2", - description: "Subject identifier", + name: "custom_claim", + description: "Test custom claim", is_active: true, is_mandatory: true, is_aws_tag: false, @@ -1046,8 +1049,8 @@ defmodule Secrethub.InternalGrpcApi.Test do test "with empty org_id returns error", %{project_id: project_id, channel: channel} do claim_config = ClaimConfig.new( - name: "sub2", - description: "Subject identifier", + name: "custom_claim", + description: "Test custom claim", is_active: true, is_mandatory: true, is_aws_tag: false, @@ -1161,8 +1164,8 @@ defmodule Secrethub.InternalGrpcApi.Test do # Set up initial JWT configuration claim_config = ClaimConfig.new( - name: "sub2", - description: "Subject identifier", + name: "custom_claim", + description: "Test custom claim", is_active: true, is_mandatory: true, is_aws_tag: false, @@ -1221,8 +1224,8 @@ defmodule Secrethub.InternalGrpcApi.Test do project_id: "", claims: [ ClaimConfig.new( - name: "sub2", - description: "Subject identifier", + name: "custom_claim", + description: "Test custom claim", is_active: true, is_mandatory: true, is_aws_tag: false, @@ -1249,8 +1252,8 @@ defmodule Secrethub.InternalGrpcApi.Test do refute is_nil(response.project_id), "Expected project_id not to be nil" expected_claim = %{ - name: "sub2", - description: "Subject identifier", + name: "custom_claim", + description: "Test custom claim", is_active: true, is_mandatory: false, is_aws_tag: false,