Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ these are placement, naming, typing and documentation changes made before the fi
- [x] E2: rename the moved sentinels to `ErrMissing`, `ErrAmbiguous` and `ErrInvalid`.
- [x] E3: rename `identity.WithCaller` to `identity.NewContext`.
- [x] E4: type the `InvocationError` operation and limit names with exported constants.
- [ ] E5: add `String` methods to the `identity` enumerations.
- [x] E5: add `String` methods to the `identity` enumerations.
- [ ] E6: complete sentinel docs, doc links, callback parameter names and `Claim.Bool` result names.

Acceptance: each PR passes the full AGENTS.md validation set, and examples, guides and the external consumer check use the new names.
Expand Down
4 changes: 1 addition & 3 deletions identity/caller.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package identity

import "strconv"

// Kind identifies the caller's mutually exclusive identity representation.
type Kind uint8

Expand Down Expand Up @@ -71,7 +69,7 @@ func (c Caller) IAM() (IAM, bool) {

// String describes kind and source without exposing identifiers or claims.
func (c Caller) String() string {
return "identity.Caller{kind:" + strconv.Itoa(int(c.Kind())) + ",source:" + strconv.Itoa(int(c.source)) + "}"
return "identity.Caller{kind:" + c.Kind().String() + ",source:" + c.source.String() + "}"
}

// GoString returns the same sanitized description as String.
Expand Down
67 changes: 67 additions & 0 deletions identity/names.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package identity

import "strconv"

var (
kindNames = [...]string{
KindAnonymous: "anonymous",
KindJWT: "jwt",
KindIAM: "iam",
}
sourceNames = [...]string{
SourceNone: "none",
SourceGatewayAssertion: "gateway_assertion",
SourceLocallyVerifiedToken: "locally_verified_token",
SourceCustomAssertion: "custom_assertion",
SourceVerifiedIAMProof: "verified_iam_proof",
}
claimKindNames = [...]string{
ClaimInvalid: "invalid",
ClaimNull: "null",
ClaimBoolean: "boolean",
ClaimString: "string",
ClaimNumber: "number",
ClaimArray: "array",
ClaimObject: "object",
}
representationNames = [...]string{
RepresentationUnknown: "unknown",
RepresentationJSON: "json",
RepresentationDecoded: "decoded",
RepresentationGatewayText: "gateway_text",
}
iamPrincipalTypeNames = [...]string{
IAMPrincipalUnknown: "unknown",
IAMPrincipalRoot: "root",
IAMPrincipalUser: "user",
IAMPrincipalAssumedRole: "assumed_role",
IAMPrincipalFederatedUser: "federated_user",
}
)

// enumName returns names[v], or typeName(v) for a value outside the declared set.
func enumName(names []string, typeName string, v uint8) string {
if int(v) < len(names) {
return names[v]
}
return typeName + "(" + strconv.Itoa(int(v)) + ")"
}

// String returns a stable lowercase name such as "jwt", or "Kind(n)" for an undeclared value.
func (k Kind) String() string { return enumName(kindNames[:], "Kind", uint8(k)) }

// String returns a stable lowercase name such as "verified_iam_proof", or "Source(n)" for an undeclared value.
func (s Source) String() string { return enumName(sourceNames[:], "Source", uint8(s)) }

// String returns a stable lowercase name such as "number", or "ClaimKind(n)" for an undeclared value.
func (k ClaimKind) String() string { return enumName(claimKindNames[:], "ClaimKind", uint8(k)) }

// String returns a stable lowercase name such as "gateway_text", or "Representation(n)" for an undeclared value.
func (r Representation) String() string {
return enumName(representationNames[:], "Representation", uint8(r))
}

// String returns a stable lowercase name such as "assumed_role", or "IAMPrincipalType(n)" for an undeclared value.
func (t IAMPrincipalType) String() string {
return enumName(iamPrincipalTypeNames[:], "IAMPrincipalType", uint8(t))
}
70 changes: 70 additions & 0 deletions identity/names_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package identity_test

import (
"fmt"
"strings"
"testing"

"github.com/asteroid-computing/go-lambda-edge/identity"
)

func TestEnumerationStrings(t *testing.T) {
for _, tt := range []struct {
value fmt.Stringer
want string
}{
{identity.KindAnonymous, "anonymous"},
{identity.KindJWT, "jwt"},
{identity.KindIAM, "iam"},
{identity.Kind(3), "Kind(3)"},
{identity.SourceNone, "none"},
{identity.SourceGatewayAssertion, "gateway_assertion"},
{identity.SourceLocallyVerifiedToken, "locally_verified_token"},
{identity.SourceCustomAssertion, "custom_assertion"},
{identity.SourceVerifiedIAMProof, "verified_iam_proof"},
{identity.Source(255), "Source(255)"},
{identity.ClaimInvalid, "invalid"},
{identity.ClaimNull, "null"},
{identity.ClaimBoolean, "boolean"},
{identity.ClaimString, "string"},
{identity.ClaimNumber, "number"},
{identity.ClaimArray, "array"},
{identity.ClaimObject, "object"},
{identity.ClaimKind(7), "ClaimKind(7)"},
{identity.RepresentationUnknown, "unknown"},
{identity.RepresentationJSON, "json"},
{identity.RepresentationDecoded, "decoded"},
{identity.RepresentationGatewayText, "gateway_text"},
{identity.Representation(4), "Representation(4)"},
{identity.IAMPrincipalUnknown, "unknown"},
{identity.IAMPrincipalRoot, "root"},
{identity.IAMPrincipalUser, "user"},
{identity.IAMPrincipalAssumedRole, "assumed_role"},
{identity.IAMPrincipalFederatedUser, "federated_user"},
{identity.IAMPrincipalType(5), "IAMPrincipalType(5)"},
} {
if got := tt.value.String(); got != tt.want {
t.Errorf("%T(%d).String() = %q, want %q", tt.value, tt.value, got, tt.want)
}
if got := fmt.Sprintf("%v", tt.value); got != tt.want {
t.Errorf("fmt %%v of %T = %q, want %q", tt.value, got, tt.want)
}
}
}

func TestCallerStringNamesKindAndSourceOnly(t *testing.T) {
caller, err := identity.NewIAM("arn:aws:iam::123456789012:user/Alice", identity.SourceVerifiedIAMProof)
if err != nil {
t.Fatal(err)
}
const want = "identity.Caller{kind:iam,source:verified_iam_proof}"
if got := caller.String(); got != want {
t.Errorf("Caller.String() = %q, want %q", got, want)
}
if got := fmt.Sprintf("%#v", caller); got != want || strings.Contains(got, "Alice") || strings.Contains(got, "123456789012") {
t.Errorf("Caller GoString = %q, want %q without identifiers", got, want)
}
if got, want := (identity.Caller{}).String(), "identity.Caller{kind:anonymous,source:none}"; got != want {
t.Errorf("anonymous Caller.String() = %q, want %q", got, want)
}
}
Loading