Skip to content

Commit

Permalink
Merge pull request #52 from synadia-io/ephemeral-user
Browse files Browse the repository at this point in the history
Ephemeral user
  • Loading branch information
aricart authored Dec 16, 2024
2 parents 7f27d16 + 37e8da3 commit ce28539
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 7 deletions.
3 changes: 3 additions & 0 deletions providers/kv/kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,9 @@ func (p *KvProvider) Store(operators []*ab.OperatorData) error {
return err
}
for _, u := range a.UserDatas {
if u.Ephemeral {
continue
}
if err := p.StoreUser(u); err != nil {
return err
}
Expand Down
3 changes: 3 additions & 0 deletions providers/nsc/nsc.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ func (a *NscProvider) Store(operators []*authb.OperatorData) error {
}

for _, u := range account.UserDatas {
if u.Ephemeral {
continue
}
if u.Modified {
if err := s.StoreRaw([]byte(u.Token)); err != nil {
return err
Expand Down
78 changes: 78 additions & 0 deletions tests/users_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tests

import (
"github.com/nats-io/nkeys"
"time"

"github.com/nats-io/jwt/v2"
Expand Down Expand Up @@ -478,3 +479,80 @@ func (t *ProviderSuite) Test_SetUserPermissionLimits() {
t.True(u.BearerToken())
t.Contains(u.SubPermissions().Allow(), "hello")
}

func (t *ProviderSuite) Test_AddEphemeralUserWithIdentity() {
auth, err := authb.NewAuth(t.Provider)
t.NoError(err)
o, err := auth.Operators().Add("O")
t.NoError(err)
t.NotNil(o)
a, err := o.Accounts().Add("A")
t.NoError(err)
t.NotNil(a)

uk, err := authb.KeyFor(nkeys.PrefixByteUser)
t.NoError(err)

u, err := a.Users().AddWithIdentity("U", "", uk.Public)
t.NoError(err)
t.Equal(u.Subject(), uk.Public)
t.True(u.(*authb.UserData).Ephemeral)

// should fail creds
_, err = u.Creds(time.Second)
t.Error(err)

t.NoError(auth.Commit())
t.NoError(auth.Reload())

o, err = auth.Operators().Get("O")
t.NoError(err)
a, err = o.Accounts().Get("A")
t.NoError(err)
_, err = a.Users().Get("U")
t.Error(err, authb.ErrNotFound)
}

func (t *ProviderSuite) Test_AddWithIdentity() {
auth, err := authb.NewAuth(t.Provider)
t.NoError(err)
o, err := auth.Operators().Add("O")
t.NoError(err)
t.NotNil(o)
a, err := o.Accounts().Add("A")
t.NoError(err)
t.NotNil(a)

uk, err := authb.KeyFor(nkeys.PrefixByteUser)
t.NoError(err)

u, err := a.Users().AddWithIdentity("U", "", string(uk.Seed))
t.NoError(err)
t.Equal(u.Subject(), uk.Public)
t.False(u.(*authb.UserData).Ephemeral)

t.NoError(auth.Commit())
t.NoError(auth.Reload())

o, err = auth.Operators().Get("O")
t.NoError(err)
a, err = o.Accounts().Get("A")
t.NoError(err)
u, err = a.Users().Get("U")
t.NoError(err)
t.NotNil(u)
}

func (t *ProviderSuite) Test_AddWithIdentityRequiresUser() {
auth, err := authb.NewAuth(t.Provider)
t.NoError(err)
o, err := auth.Operators().Add("O")
t.NoError(err)
t.NotNil(o)
a, err := o.Accounts().Add("A")
t.NoError(err)
t.NotNil(a)

_, err = a.Users().AddWithIdentity("U", "", "")
t.Error(err)
}
10 changes: 8 additions & 2 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ type UserData struct {
AccountData *AccountData
RejectEdits bool
Claim *jwt.UserClaims
Ephemeral bool
}

func (u *UserData) MarshalJSON() ([]byte, error) {
Expand Down Expand Up @@ -335,11 +336,16 @@ type Account interface {
// Users is an interface for managing users
type Users interface {
// Add creates a new User with the specified name and signed using
// the specified key. Note that you simply specify the public key
// the specified signer key. Note that you simply specify the public key
// you want to use for signing, and the key must be one of the account's
// signing keys. If the key is associated with a scope, the user will
// be a scoped user.
Add(name string, key string) (User, error)
Add(name string, signer string) (User, error)
// AddWithIdentity creates user with the specified name and signed using
// the specified signer key.
// If the provided ID is only a public key the user will be ephemeral and will not stored,
// other operations, as cred generation will fail
AddWithIdentity(name string, signer string, id string) (User, error)
// Delete the user by matching its name or subject
Delete(name string) error
// Get returns the user by matching its name or subject
Expand Down
26 changes: 21 additions & 5 deletions users.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ type UsersImpl struct {
}

func (a *UsersImpl) Add(name string, key string) (User, error) {
uk, err := a.accountData.Operator.SigningService.NewKey(nkeys.PrefixByteUser)
if err != nil {
return nil, err
}
return a.add(name, key, uk)
}

func (a *UsersImpl) add(name string, key string, uk *Key) (User, error) {
if key == "" {
key = a.accountData.Key.Public
}
Expand All @@ -18,15 +26,13 @@ func (a *UsersImpl) Add(name string, key string) (User, error) {
return nil, err
}
_, scoped := a.accountData.Claim.SigningKeys.GetScope(key)
uk, err := a.accountData.Operator.SigningService.NewKey(nkeys.PrefixByteUser)
if err != nil {
return nil, err
}

d := &UserData{
BaseData: BaseData{EntityName: name, Key: uk, Modified: true},
AccountData: a.accountData,
Claim: jwt.NewUserClaims(uk.Public),
RejectEdits: scoped,
Ephemeral: uk.Seed == nil,
}
d.Claim.Name = name
if signingKey {
Expand All @@ -41,10 +47,20 @@ func (a *UsersImpl) Add(name string, key string) (User, error) {
return nil, err
}
a.accountData.UserDatas = append(a.accountData.UserDatas, d)
a.accountData.Operator.AddedKeys = append(a.accountData.Operator.AddedKeys, uk)
if !d.Ephemeral {
a.accountData.Operator.AddedKeys = append(a.accountData.Operator.AddedKeys, uk)
}
return d, nil
}

func (a *UsersImpl) AddWithIdentity(name string, key string, id string) (User, error) {
uk, err := KeyFrom(id, nkeys.PrefixByteUser)
if err != nil {
return nil, err
}
return a.add(name, key, uk)
}

func (a *UsersImpl) Get(name string) (User, error) {
for _, u := range a.accountData.UserDatas {
if u.EntityName == name || u.Claim.Subject == name {
Expand Down

0 comments on commit ce28539

Please sign in to comment.