From b4c6a8a0f9ade6f64117bc9f7106d6557e14def6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Mon, 25 Apr 2022 15:00:45 +0000 Subject: [PATCH 1/4] in memory user and auth provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jörn Friedrich Dreyer --- changelog/unreleased/user-provider-memory.md | 5 + pkg/auth/manager/loader/loader.go | 1 + pkg/auth/manager/memory/memory.go | 118 ++++++++++++ pkg/auth/manager/memory/memory_test.go | 58 ++++++ pkg/user/manager/loader/loader.go | 1 + pkg/user/manager/memory/memory.go | 181 +++++++++++++++++++ pkg/user/manager/memory/memory_test.go | 124 +++++++++++++ 7 files changed, 488 insertions(+) create mode 100644 changelog/unreleased/user-provider-memory.md create mode 100644 pkg/auth/manager/memory/memory.go create mode 100644 pkg/auth/manager/memory/memory_test.go create mode 100644 pkg/user/manager/memory/memory.go create mode 100644 pkg/user/manager/memory/memory_test.go diff --git a/changelog/unreleased/user-provider-memory.md b/changelog/unreleased/user-provider-memory.md new file mode 100644 index 0000000000..622ce7b585 --- /dev/null +++ b/changelog/unreleased/user-provider-memory.md @@ -0,0 +1,5 @@ +Enhancement: in memory auth and user provider + +We added an in memory implementation for the auth and user providers that reads the users from the mapstructure passed in. + +https://github.com/cs3org/reva/pull/2781 diff --git a/pkg/auth/manager/loader/loader.go b/pkg/auth/manager/loader/loader.go index 694cd98c29..5a2bd76431 100644 --- a/pkg/auth/manager/loader/loader.go +++ b/pkg/auth/manager/loader/loader.go @@ -26,6 +26,7 @@ import ( _ "github.com/cs3org/reva/v2/pkg/auth/manager/json" _ "github.com/cs3org/reva/v2/pkg/auth/manager/ldap" _ "github.com/cs3org/reva/v2/pkg/auth/manager/machine" + _ "github.com/cs3org/reva/v2/pkg/auth/manager/memory" _ "github.com/cs3org/reva/v2/pkg/auth/manager/nextcloud" _ "github.com/cs3org/reva/v2/pkg/auth/manager/oidc" _ "github.com/cs3org/reva/v2/pkg/auth/manager/owncloudsql" diff --git a/pkg/auth/manager/memory/memory.go b/pkg/auth/manager/memory/memory.go new file mode 100644 index 0000000000..f30c952dfb --- /dev/null +++ b/pkg/auth/manager/memory/memory.go @@ -0,0 +1,118 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package memory + +import ( + "context" + + authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1" + user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/v2/pkg/auth" + "github.com/cs3org/reva/v2/pkg/auth/manager/registry" + "github.com/cs3org/reva/v2/pkg/auth/scope" + "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" +) + +func init() { + registry.Register("memory", New) +} + +type config struct { + // Users holds a map with userid and secret + Users map[string]*Credentials `mapstructure:"users"` +} + +// Credentials holds a pair of secret and userid +type Credentials struct { + ID *user.UserId `mapstructure:"id" json:"id"` + Username string `mapstructure:"username" json:"username"` + Mail string `mapstructure:"mail" json:"mail"` + MailVerified bool `mapstructure:"mail_verified" json:"mail_verified"` + DisplayName string `mapstructure:"display_name" json:"display_name"` + Secret string `mapstructure:"secret" json:"secret"` + Groups []string `mapstructure:"groups" json:"groups"` + UIDNumber int64 `mapstructure:"uid_number" json:"uid_number"` + GIDNumber int64 `mapstructure:"gid_number" json:"gid_number"` + Opaque *typespb.Opaque `mapstructure:"opaque" json:"opaque"` +} + +func parseConfig(m map[string]interface{}) (*config, error) { + c := &config{} + if err := mapstructure.Decode(m, c); err != nil { + err = errors.Wrap(err, "error decoding conf") + return nil, err + } + return c, nil +} + +type manager struct { + credentials map[string]*Credentials +} + +// New returns a new auth Manager. +func New(m map[string]interface{}) (auth.Manager, error) { + mgr := &manager{} + err := mgr.Configure(m) + return mgr, err +} + +func (m *manager) Configure(ml map[string]interface{}) error { + c, err := parseConfig(ml) + if err != nil { + return err + } + m.credentials = c.Users + return nil +} + +func (m *manager) Authenticate(ctx context.Context, clientID, clientSecret string) (*user.User, map[string]*authpb.Scope, error) { + if u, ok := m.credentials[clientID]; ok { + if u.Secret == clientSecret { + var scopes map[string]*authpb.Scope + var err error + if u.ID != nil && (u.ID.Type == user.UserType_USER_TYPE_LIGHTWEIGHT || u.ID.Type == user.UserType_USER_TYPE_FEDERATED) { + scopes, err = scope.AddLightweightAccountScope(authpb.Role_ROLE_OWNER, nil) + if err != nil { + return nil, nil, err + } + } else { + scopes, err = scope.AddOwnerScope(nil) + if err != nil { + return nil, nil, err + } + } + return &user.User{ + Id: u.ID, + Username: u.Username, + Mail: u.Mail, + MailVerified: u.MailVerified, + DisplayName: u.DisplayName, + Groups: u.Groups, + UidNumber: u.UIDNumber, + GidNumber: u.GIDNumber, + Opaque: u.Opaque, + // TODO add arbitrary keys as opaque data + }, scopes, nil + } + } + return nil, nil, errtypes.InvalidCredentials(clientID) +} diff --git a/pkg/auth/manager/memory/memory_test.go b/pkg/auth/manager/memory/memory_test.go new file mode 100644 index 0000000000..3e4d29a3a3 --- /dev/null +++ b/pkg/auth/manager/memory/memory_test.go @@ -0,0 +1,58 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package memory + +import ( + "context" + "testing" +) + +var ctx = context.Background() + +func TestUserManager(t *testing.T) { + // get manager + manager, _ := New(map[string]interface{}{ + "users": map[string]interface{}{ + "admin": map[string]interface{}{ + "id": map[string]interface{}{ + "opaqueId": "ddc2004c-0977-11eb-9d3f-a793888cd0f8", + "idp": "http://localhost:9998", + "type": 1, // user.UserType_USER_TYPE_PRIMARY + }, + "username": "admin", + "secret": "admin", + "mail": "admin@example.org", + "display_name": "Administrator", + }, + }, + }) + + // Authenticate - positive test + _, _, err := manager.Authenticate(ctx, "admin", "admin") + if err != nil { + t.Fatalf("error while authenticate with correct credentials") + } + + // Authenticate - negative test + _, _, err = manager.Authenticate(ctx, "admin", "NotARealPassword") + if err == nil { + t.Fatalf("no error (but we expected one) while authenticate with bad credentials") + } + +} diff --git a/pkg/user/manager/loader/loader.go b/pkg/user/manager/loader/loader.go index d14d04edc8..ec538b093b 100644 --- a/pkg/user/manager/loader/loader.go +++ b/pkg/user/manager/loader/loader.go @@ -23,6 +23,7 @@ import ( _ "github.com/cs3org/reva/v2/pkg/user/manager/demo" _ "github.com/cs3org/reva/v2/pkg/user/manager/json" _ "github.com/cs3org/reva/v2/pkg/user/manager/ldap" + _ "github.com/cs3org/reva/v2/pkg/user/manager/memory" _ "github.com/cs3org/reva/v2/pkg/user/manager/nextcloud" _ "github.com/cs3org/reva/v2/pkg/user/manager/owncloudsql" // Add your own here diff --git a/pkg/user/manager/memory/memory.go b/pkg/user/manager/memory/memory.go new file mode 100644 index 0000000000..068fc0c000 --- /dev/null +++ b/pkg/user/manager/memory/memory.go @@ -0,0 +1,181 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package memory + +import ( + "context" + "strconv" + "strings" + + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/v2/pkg/errtypes" + "github.com/cs3org/reva/v2/pkg/user" + "github.com/cs3org/reva/v2/pkg/user/manager/registry" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" +) + +func init() { + registry.Register("memory", New) +} + +type config struct { + // Users holds a map with userid and user + Users map[string]*User `mapstructure:"users"` +} + +// User holds a user but uses _ in mapstructure names +type User struct { + ID *userpb.UserId `mapstructure:"id" json:"id"` + Username string `mapstructure:"username" json:"username"` + Mail string `mapstructure:"mail" json:"mail"` + MailVerified bool `mapstructure:"mail_verified" json:"mail_verified"` + DisplayName string `mapstructure:"display_name" json:"display_name"` + Groups []string `mapstructure:"groups" json:"groups"` + UIDNumber int64 `mapstructure:"uid_number" json:"uid_number"` + GIDNumber int64 `mapstructure:"gid_number" json:"gid_number"` + Opaque *typespb.Opaque `mapstructure:"opaque" json:"opaque"` +} + +func parseConfig(m map[string]interface{}) (*config, error) { + c := &config{} + if err := mapstructure.Decode(m, c); err != nil { + err = errors.Wrap(err, "error decoding conf") + return nil, err + } + return c, nil +} + +type manager struct { + catalog map[string]*User +} + +// New returns a new user manager. +func New(m map[string]interface{}) (user.Manager, error) { + mgr := &manager{} + err := mgr.Configure(m) + return mgr, err +} + +func (m *manager) Configure(ml map[string]interface{}) error { + c, err := parseConfig(ml) + if err != nil { + return err + } + m.catalog = c.Users + return nil +} + +func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) { + if user, ok := m.catalog[uid.OpaqueId]; ok { + if uid.Idp == "" || user.ID.Idp == uid.Idp { + u := *user + if skipFetchingGroups { + u.Groups = nil + } + return &userpb.User{ + Id: u.ID, + Username: u.Username, + Mail: u.Mail, + DisplayName: u.DisplayName, + MailVerified: u.MailVerified, + Groups: u.Groups, + Opaque: u.Opaque, + UidNumber: u.UIDNumber, + GidNumber: u.GIDNumber, + }, nil + } + } + return nil, errtypes.NotFound(uid.OpaqueId) +} + +func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) { + for _, u := range m.catalog { + if userClaim, err := extractClaim(u, claim); err == nil && value == userClaim { + user := &userpb.User{ + Id: u.ID, + Username: u.Username, + Mail: u.Mail, + DisplayName: u.DisplayName, + MailVerified: u.MailVerified, + Groups: u.Groups, + Opaque: u.Opaque, + UidNumber: u.UIDNumber, + GidNumber: u.GIDNumber, + } + if skipFetchingGroups { + user.Groups = nil + } + return user, nil + } + } + return nil, errtypes.NotFound(value) +} + +func extractClaim(u *User, claim string) (string, error) { + switch claim { + case "mail": + return u.Mail, nil + case "username": + return u.Username, nil + case "uid": + if u.UIDNumber != 0 { + return strconv.FormatInt(u.UIDNumber, 10), nil + } + } + return "", errors.New("memory: invalid field") +} + +// TODO(jfd) compare sub? +func userContains(u *User, query string) bool { + return strings.Contains(u.Username, query) || strings.Contains(u.DisplayName, query) || strings.Contains(u.Mail, query) || strings.Contains(u.ID.OpaqueId, query) +} + +func (m *manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { + users := []*userpb.User{} + for _, u := range m.catalog { + if userContains(u, query) { + user := &userpb.User{ + Id: u.ID, + Username: u.Username, + Mail: u.Mail, + DisplayName: u.DisplayName, + MailVerified: u.MailVerified, + Groups: u.Groups, + Opaque: u.Opaque, + UidNumber: u.UIDNumber, + GidNumber: u.GIDNumber, + } + if skipFetchingGroups { + user.Groups = nil + } + users = append(users, user) + } + } + return users, nil +} + +func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) { + user, err := m.GetUser(ctx, uid, false) + if err != nil { + return nil, err + } + return user.Groups, nil +} diff --git a/pkg/user/manager/memory/memory_test.go b/pkg/user/manager/memory/memory_test.go new file mode 100644 index 0000000000..ed1e0a4d5f --- /dev/null +++ b/pkg/user/manager/memory/memory_test.go @@ -0,0 +1,124 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package memory + +import ( + "context" + "reflect" + "testing" + + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + "github.com/cs3org/reva/v2/pkg/errtypes" +) + +var ctx = context.Background() + +func TestUserManager(t *testing.T) { + // get manager + manager, _ := New(map[string]interface{}{ + "users": map[string]interface{}{ + "4c510ada-c86b-4815-8820-42cdf82c3d51": map[string]interface{}{ + "id": map[string]interface{}{ + "opaqueId": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "idp": "http://localhost:9998", + "type": 1, // user.UserType_USER_TYPE_PRIMARY + }, + "uid_number": 123, + "gid_number": 987, + "username": "einstein", + "mail": "einstein@example.org", + "display_name": "Albert Einstein", + "groups": []string{"sailing-lovers", "violin-haters", "physics-lovers"}, + }, + }, + }) + + // setup test data + uidEinstein := &userpb.UserId{Idp: "http://localhost:9998", OpaqueId: "4c510ada-c86b-4815-8820-42cdf82c3d51", Type: userpb.UserType_USER_TYPE_PRIMARY} + userEinstein := &userpb.User{ + Id: uidEinstein, + Username: "einstein", + Groups: []string{"sailing-lovers", "violin-haters", "physics-lovers"}, + Mail: "einstein@example.org", + DisplayName: "Albert Einstein", + UidNumber: 123, + GidNumber: 987, + } + userEinsteinWithoutGroups := &userpb.User{ + Id: uidEinstein, + Username: "einstein", + Mail: "einstein@example.org", + DisplayName: "Albert Einstein", + UidNumber: 123, + GidNumber: 987, + } + + uidFake := &userpb.UserId{Idp: "nonesense", OpaqueId: "fakeUser"} + groupsEinstein := []string{"sailing-lovers", "violin-haters", "physics-lovers"} + + // positive test GetUserByClaim by uid + resUserByUID, _ := manager.GetUserByClaim(ctx, "uid", "123", false) + if !reflect.DeepEqual(resUserByUID, userEinstein) { + t.Fatalf("user differs: expected=%v got=%v", userEinstein, resUserByUID) + } + + // negative test GetUserByClaim by uid + expectedErr := errtypes.NotFound("789") + _, err := manager.GetUserByClaim(ctx, "uid", "789", false) + if !reflect.DeepEqual(err, expectedErr) { + t.Fatalf("user not found error differs: expected='%v' got='%v'", expectedErr, err) + } + + // positive test GetUserByClaim by mail + resUserByEmail, _ := manager.GetUserByClaim(ctx, "mail", "einstein@example.org", false) + if !reflect.DeepEqual(resUserByEmail, userEinstein) { + t.Fatalf("user differs: expected=%v got=%v", userEinstein, resUserByEmail) + } + + // positive test GetUserByClaim by uid without groups + resUserByUIDWithoutGroups, _ := manager.GetUserByClaim(ctx, "uid", "123", true) + if !reflect.DeepEqual(resUserByUIDWithoutGroups, userEinsteinWithoutGroups) { + t.Fatalf("user differs: expected=%v got=%v", userEinsteinWithoutGroups, resUserByUIDWithoutGroups) + } + + // positive test GetUserGroups + resGroups, _ := manager.GetUserGroups(ctx, uidEinstein) + if !reflect.DeepEqual(resGroups, groupsEinstein) { + t.Fatalf("groups differ: expected=%v got=%v", groupsEinstein, resGroups) + } + + // negative test GetUserGroups + expectedErr = errtypes.NotFound(uidFake.OpaqueId) + _, err = manager.GetUserGroups(ctx, uidFake) + if !reflect.DeepEqual(err, expectedErr) { + t.Fatalf("user not found error differs: expected='%v' got='%v'", expectedErr, err) + } + + // test FindUsers + resUser, _ := manager.FindUsers(ctx, "einstein", false) + if !reflect.DeepEqual(resUser, []*userpb.User{userEinstein}) { + t.Fatalf("user differs: expected=%v got=%v", []*userpb.User{userEinstein}, resUser) + } + + // negative test FindUsers + resUsers, _ := manager.FindUsers(ctx, "notARealUser", false) + if len(resUsers) > 0 { + t.Fatalf("user not in group: expected=%v got=%v", []*userpb.User{}, resUsers) + } +} From 9d02a1e730b5ef90df20365766afb93ac3ac0ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Wed, 27 Apr 2022 14:18:21 +0000 Subject: [PATCH 2/4] add get by userid claim support to memory and demo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jörn Friedrich Dreyer --- pkg/user/manager/demo/demo.go | 2 ++ pkg/user/manager/memory/memory.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pkg/user/manager/demo/demo.go b/pkg/user/manager/demo/demo.go index 5a429fa002..edadeeb970 100644 --- a/pkg/user/manager/demo/demo.go +++ b/pkg/user/manager/demo/demo.go @@ -86,6 +86,8 @@ func extractClaim(u *userpb.User, claim string) (string, error) { return u.Mail, nil case "username": return u.Username, nil + case "userid": + return u.Id.OpaqueId, nil case "uid": if u.UidNumber != 0 { return strconv.FormatInt(u.UidNumber, 10), nil diff --git a/pkg/user/manager/memory/memory.go b/pkg/user/manager/memory/memory.go index 068fc0c000..6b9bffa5e1 100644 --- a/pkg/user/manager/memory/memory.go +++ b/pkg/user/manager/memory/memory.go @@ -135,6 +135,8 @@ func extractClaim(u *User, claim string) (string, error) { return u.Mail, nil case "username": return u.Username, nil + case "userid": + return u.ID.OpaqueId, nil case "uid": if u.UIDNumber != 0 { return strconv.FormatInt(u.UIDNumber, 10), nil From 98c01c313eb9e59c9eb78d81213efad4d12fdaf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Wed, 27 Apr 2022 14:19:16 +0000 Subject: [PATCH 3/4] get rid of in memory auth provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jörn Friedrich Dreyer --- pkg/auth/manager/loader/loader.go | 1 - pkg/auth/manager/memory/memory.go | 118 ------------------------- pkg/auth/manager/memory/memory_test.go | 58 ------------ 3 files changed, 177 deletions(-) delete mode 100644 pkg/auth/manager/memory/memory.go delete mode 100644 pkg/auth/manager/memory/memory_test.go diff --git a/pkg/auth/manager/loader/loader.go b/pkg/auth/manager/loader/loader.go index 5a2bd76431..694cd98c29 100644 --- a/pkg/auth/manager/loader/loader.go +++ b/pkg/auth/manager/loader/loader.go @@ -26,7 +26,6 @@ import ( _ "github.com/cs3org/reva/v2/pkg/auth/manager/json" _ "github.com/cs3org/reva/v2/pkg/auth/manager/ldap" _ "github.com/cs3org/reva/v2/pkg/auth/manager/machine" - _ "github.com/cs3org/reva/v2/pkg/auth/manager/memory" _ "github.com/cs3org/reva/v2/pkg/auth/manager/nextcloud" _ "github.com/cs3org/reva/v2/pkg/auth/manager/oidc" _ "github.com/cs3org/reva/v2/pkg/auth/manager/owncloudsql" diff --git a/pkg/auth/manager/memory/memory.go b/pkg/auth/manager/memory/memory.go deleted file mode 100644 index f30c952dfb..0000000000 --- a/pkg/auth/manager/memory/memory.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018-2021 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package memory - -import ( - "context" - - authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1" - user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" - "github.com/cs3org/reva/v2/pkg/auth" - "github.com/cs3org/reva/v2/pkg/auth/manager/registry" - "github.com/cs3org/reva/v2/pkg/auth/scope" - "github.com/cs3org/reva/v2/pkg/errtypes" - "github.com/mitchellh/mapstructure" - "github.com/pkg/errors" -) - -func init() { - registry.Register("memory", New) -} - -type config struct { - // Users holds a map with userid and secret - Users map[string]*Credentials `mapstructure:"users"` -} - -// Credentials holds a pair of secret and userid -type Credentials struct { - ID *user.UserId `mapstructure:"id" json:"id"` - Username string `mapstructure:"username" json:"username"` - Mail string `mapstructure:"mail" json:"mail"` - MailVerified bool `mapstructure:"mail_verified" json:"mail_verified"` - DisplayName string `mapstructure:"display_name" json:"display_name"` - Secret string `mapstructure:"secret" json:"secret"` - Groups []string `mapstructure:"groups" json:"groups"` - UIDNumber int64 `mapstructure:"uid_number" json:"uid_number"` - GIDNumber int64 `mapstructure:"gid_number" json:"gid_number"` - Opaque *typespb.Opaque `mapstructure:"opaque" json:"opaque"` -} - -func parseConfig(m map[string]interface{}) (*config, error) { - c := &config{} - if err := mapstructure.Decode(m, c); err != nil { - err = errors.Wrap(err, "error decoding conf") - return nil, err - } - return c, nil -} - -type manager struct { - credentials map[string]*Credentials -} - -// New returns a new auth Manager. -func New(m map[string]interface{}) (auth.Manager, error) { - mgr := &manager{} - err := mgr.Configure(m) - return mgr, err -} - -func (m *manager) Configure(ml map[string]interface{}) error { - c, err := parseConfig(ml) - if err != nil { - return err - } - m.credentials = c.Users - return nil -} - -func (m *manager) Authenticate(ctx context.Context, clientID, clientSecret string) (*user.User, map[string]*authpb.Scope, error) { - if u, ok := m.credentials[clientID]; ok { - if u.Secret == clientSecret { - var scopes map[string]*authpb.Scope - var err error - if u.ID != nil && (u.ID.Type == user.UserType_USER_TYPE_LIGHTWEIGHT || u.ID.Type == user.UserType_USER_TYPE_FEDERATED) { - scopes, err = scope.AddLightweightAccountScope(authpb.Role_ROLE_OWNER, nil) - if err != nil { - return nil, nil, err - } - } else { - scopes, err = scope.AddOwnerScope(nil) - if err != nil { - return nil, nil, err - } - } - return &user.User{ - Id: u.ID, - Username: u.Username, - Mail: u.Mail, - MailVerified: u.MailVerified, - DisplayName: u.DisplayName, - Groups: u.Groups, - UidNumber: u.UIDNumber, - GidNumber: u.GIDNumber, - Opaque: u.Opaque, - // TODO add arbitrary keys as opaque data - }, scopes, nil - } - } - return nil, nil, errtypes.InvalidCredentials(clientID) -} diff --git a/pkg/auth/manager/memory/memory_test.go b/pkg/auth/manager/memory/memory_test.go deleted file mode 100644 index 3e4d29a3a3..0000000000 --- a/pkg/auth/manager/memory/memory_test.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018-2021 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package memory - -import ( - "context" - "testing" -) - -var ctx = context.Background() - -func TestUserManager(t *testing.T) { - // get manager - manager, _ := New(map[string]interface{}{ - "users": map[string]interface{}{ - "admin": map[string]interface{}{ - "id": map[string]interface{}{ - "opaqueId": "ddc2004c-0977-11eb-9d3f-a793888cd0f8", - "idp": "http://localhost:9998", - "type": 1, // user.UserType_USER_TYPE_PRIMARY - }, - "username": "admin", - "secret": "admin", - "mail": "admin@example.org", - "display_name": "Administrator", - }, - }, - }) - - // Authenticate - positive test - _, _, err := manager.Authenticate(ctx, "admin", "admin") - if err != nil { - t.Fatalf("error while authenticate with correct credentials") - } - - // Authenticate - negative test - _, _, err = manager.Authenticate(ctx, "admin", "NotARealPassword") - if err == nil { - t.Fatalf("no error (but we expected one) while authenticate with bad credentials") - } - -} From eb451a135a3b7c3d912c743a58eb63971c10a1f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Wed, 27 Apr 2022 14:20:33 +0000 Subject: [PATCH 4/4] update changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jörn Friedrich Dreyer --- changelog/unreleased/user-provider-memory.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/unreleased/user-provider-memory.md b/changelog/unreleased/user-provider-memory.md index 622ce7b585..6391fe9136 100644 --- a/changelog/unreleased/user-provider-memory.md +++ b/changelog/unreleased/user-provider-memory.md @@ -1,5 +1,5 @@ -Enhancement: in memory auth and user provider +Enhancement: in memory user provider -We added an in memory implementation for the auth and user providers that reads the users from the mapstructure passed in. +We added an in memory implementation for the user provider that reads the users from the mapstructure passed in. https://github.com/cs3org/reva/pull/2781