package vault import ( "context" "testing" "time" uuid "github.com/hashicorp/go-uuid" credGithub "github.com/hashicorp/vault/builtin/credential/github" "github.com/hashicorp/vault/logical" ) func TestIdentityStore_EntityIDPassthrough(t *testing.T) { // Enable GitHub auth and initialize is, ghAccessor, core := testIdentityStoreWithGithubAuth(t) alias := &logical.Alias{ MountType: "github", MountAccessor: ghAccessor, Name: "githubuser", } // Create an entity with GitHub alias entity, err := is.CreateOrFetchEntity(alias) if err != nil { t.Fatal(err) } if entity == nil { t.Fatalf("expected a non-nil entity") } // Create a token with the above created entity set on it ent := &logical.TokenEntry{ ID: "testtokenid", Path: "test", Policies: []string{"root"}, CreationTime: time.Now().Unix(), EntityID: entity.ID, } if err := core.tokenStore.create(context.Background(), ent); err != nil { t.Fatalf("err: %s", err) } // Set a request handler to the noop backend which responds with the entity // ID received in the request object requestHandler := func(ctx context.Context, req *logical.Request) (*logical.Response, error) { return &logical.Response{ Data: map[string]interface{}{ "entity_id": req.EntityID, }, }, nil } noop := &NoopBackend{ RequestHandler: requestHandler, } // Mount the noop backend _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") meUUID, err := uuid.GenerateUUID() if err != nil { t.Fatal(err) } err = core.router.Mount(noop, "test/backend/", &MountEntry{Path: "test/backend/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) if err != nil { t.Fatal(err) } // Make the request with the above created token resp, err := core.HandleRequest(&logical.Request{ ClientToken: "testtokenid", Operation: logical.ReadOperation, Path: "test/backend/foo", }) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\n err: %v", resp, err) } // Expected entity ID to be in the response if resp.Data["entity_id"] != entity.ID { t.Fatalf("expected entity ID to be passed through to the backend") } } func TestIdentityStore_CreateOrFetchEntity(t *testing.T) { is, ghAccessor, _ := testIdentityStoreWithGithubAuth(t) alias := &logical.Alias{ MountType: "github", MountAccessor: ghAccessor, Name: "githubuser", } entity, err := is.CreateOrFetchEntity(alias) if err != nil { t.Fatal(err) } if entity == nil { t.Fatalf("expected a non-nil entity") } if len(entity.Aliases) != 1 { t.Fatalf("bad: length of aliases; expected: 1, actual: %d", len(entity.Aliases)) } if entity.Aliases[0].Name != alias.Name { t.Fatalf("bad: alias name; expected: %q, actual: %q", alias.Name, entity.Aliases[0].Name) } entity, err = is.CreateOrFetchEntity(alias) if err != nil { t.Fatal(err) } if entity == nil { t.Fatalf("expected a non-nil entity") } if len(entity.Aliases) != 1 { t.Fatalf("bad: length of aliases; expected: 1, actual: %d", len(entity.Aliases)) } if entity.Aliases[0].Name != alias.Name { t.Fatalf("bad: alias name; expected: %q, actual: %q", alias.Name, entity.Aliases[0].Name) } } func TestIdentityStore_EntityByAliasFactors(t *testing.T) { var err error var resp *logical.Response is, ghAccessor, _ := testIdentityStoreWithGithubAuth(t) registerData := map[string]interface{}{ "name": "testentityname", "metadata": []string{"someusefulkey=someusefulvalue"}, "policies": []string{"testpolicy1", "testpolicy2"}, } registerReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "entity", Data: registerData, } // Register the entity resp, err = is.HandleRequest(context.Background(), registerReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } idRaw, ok := resp.Data["id"] if !ok { t.Fatalf("entity id not present in response") } entityID := idRaw.(string) if entityID == "" { t.Fatalf("invalid entity id") } aliasData := map[string]interface{}{ "entity_id": entityID, "name": "alias_name", "mount_accessor": ghAccessor, } aliasReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "alias", Data: aliasData, } resp, err = is.HandleRequest(context.Background(), aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } if resp == nil { t.Fatalf("expected a non-nil response") } entity, err := is.entityByAliasFactors(ghAccessor, "alias_name", false) if err != nil { t.Fatal(err) } if entity == nil { t.Fatalf("expected a non-nil entity") } if entity.ID != entityID { t.Fatalf("bad: entity ID; expected: %q actual: %q", entityID, entity.ID) } } func TestIdentityStore_WrapInfoInheritance(t *testing.T) { var err error var resp *logical.Response core, is, ts, _ := testCoreWithIdentityTokenGithub(t) registerData := map[string]interface{}{ "name": "testentityname", "metadata": []string{"someusefulkey=someusefulvalue"}, "policies": []string{"testpolicy1", "testpolicy2"}, } registerReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "entity", Data: registerData, } // Register the entity resp, err = is.HandleRequest(context.Background(), registerReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } idRaw, ok := resp.Data["id"] if !ok { t.Fatalf("entity id not present in response") } entityID := idRaw.(string) if entityID == "" { t.Fatalf("invalid entity id") } // Create a token which has EntityID set and has update permissions to // sys/wrapping/wrap te := &logical.TokenEntry{ Path: "test", Policies: []string{"default", responseWrappingPolicyName}, EntityID: entityID, TTL: time.Hour, } testMakeTokenDirectly(t, ts, te) wrapReq := &logical.Request{ Path: "sys/wrapping/wrap", ClientToken: te.ID, Operation: logical.UpdateOperation, Data: map[string]interface{}{ "foo": "bar", }, WrapInfo: &logical.RequestWrapInfo{ TTL: time.Duration(5 * time.Second), }, } resp, err = core.HandleRequest(wrapReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } if resp.WrapInfo == nil { t.Fatalf("expected a non-nil WrapInfo") } if resp.WrapInfo.WrappedEntityID != entityID { t.Fatalf("bad: WrapInfo in response not having proper entity ID set; expected: %q, actual:%q", entityID, resp.WrapInfo.WrappedEntityID) } } func TestIdentityStore_TokenEntityInheritance(t *testing.T) { c, _, _ := TestCoreUnsealed(t) ts := c.tokenStore // Create a token which has EntityID set te := &logical.TokenEntry{ Path: "test", Policies: []string{"dev", "prod"}, EntityID: "testentityid", TTL: time.Hour, } testMakeTokenDirectly(t, ts, te) // Create a child token; this should inherit the EntityID tokenReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "create", ClientToken: te.ID, } resp, err := ts.HandleRequest(context.Background(), tokenReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v err: %v", err, resp) } if resp.Auth.EntityID != te.EntityID { t.Fatalf("bad: entity ID; expected: %v, actual: %v", te.EntityID, resp.Auth.EntityID) } // Create an orphan token; this should not inherit the EntityID tokenReq.Path = "create-orphan" resp, err = ts.HandleRequest(context.Background(), tokenReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v err: %v", err, resp) } if resp.Auth.EntityID != "" { t.Fatalf("expected entity ID to be not set") } } func testCoreWithIdentityTokenGithub(t *testing.T) (*Core, *IdentityStore, *TokenStore, string) { is, ghAccessor, core := testIdentityStoreWithGithubAuth(t) return core, is, core.tokenStore, ghAccessor } func testCoreWithIdentityTokenGithubRoot(t *testing.T) (*Core, *IdentityStore, *TokenStore, string, string) { is, ghAccessor, core, root := testIdentityStoreWithGithubAuthRoot(t) return core, is, core.tokenStore, ghAccessor, root } func testIdentityStoreWithGithubAuth(t *testing.T) (*IdentityStore, string, *Core) { is, ghA, c, _ := testIdentityStoreWithGithubAuthRoot(t) return is, ghA, c } // testIdentityStoreWithGithubAuthRoot returns an instance of identity store // which is mounted by default. This function also enables the github auth // backend to assist with testing aliases and entities that require an valid // mount accessor of an auth backend. func testIdentityStoreWithGithubAuthRoot(t *testing.T) (*IdentityStore, string, *Core, string) { // Add github credential factory to core config err := AddTestCredentialBackend("github", credGithub.Factory) if err != nil { t.Fatalf("err: %s", err) } c, _, root := TestCoreUnsealed(t) meGH := &MountEntry{ Table: credentialTableType, Path: "github/", Type: "github", Description: "github auth", } err = c.enableCredential(context.Background(), meGH) if err != nil { t.Fatal(err) } return c.identityStore, meGH.Accessor, c, root } func TestIdentityStore_MetadataKeyRegex(t *testing.T) { key := "validVALID012_-=+/" if !metaKeyFormatRegEx(key) { t.Fatal("failed to accept valid metadata key") } key = "a:b" if metaKeyFormatRegEx(key) { t.Fatal("accepted invalid metadata key") } }