switch to go vendoring
This commit is contained in:
+166
@@ -0,0 +1,166 @@
|
||||
// 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 demo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/errtypes"
|
||||
"github.com/cs3org/reva/v2/pkg/user"
|
||||
"github.com/cs3org/reva/v2/pkg/user/manager/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Register("demo", New)
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
catalog map[string]*userpb.User
|
||||
}
|
||||
|
||||
// New returns a new user manager.
|
||||
func New(m map[string]interface{}) (user.Manager, error) {
|
||||
mgr := &manager{}
|
||||
err := mgr.Configure(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mgr, err
|
||||
}
|
||||
|
||||
func (m *manager) Configure(ml map[string]interface{}) error {
|
||||
cat := getUsers()
|
||||
m.catalog = cat
|
||||
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 &u, 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 := *u
|
||||
if skipFetchingGroups {
|
||||
user.Groups = nil
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
}
|
||||
return nil, errtypes.NotFound(value)
|
||||
}
|
||||
|
||||
func extractClaim(u *userpb.User, claim string) (string, error) {
|
||||
switch claim {
|
||||
case "mail":
|
||||
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
|
||||
}
|
||||
}
|
||||
return "", errors.New("demo: invalid field")
|
||||
}
|
||||
|
||||
// TODO(jfd) compare sub?
|
||||
func userContains(u *userpb.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 := *u
|
||||
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
|
||||
}
|
||||
|
||||
func getUsers() map[string]*userpb.User {
|
||||
return map[string]*userpb.User{
|
||||
"4c510ada-c86b-4815-8820-42cdf82c3d51": {
|
||||
Id: &userpb.UserId{
|
||||
Idp: "http://localhost:9998",
|
||||
OpaqueId: "4c510ada-c86b-4815-8820-42cdf82c3d51",
|
||||
Type: userpb.UserType_USER_TYPE_PRIMARY,
|
||||
},
|
||||
Username: "einstein",
|
||||
Groups: []string{"sailing-lovers", "violin-haters", "physics-lovers"},
|
||||
Mail: "einstein@example.org",
|
||||
DisplayName: "Albert Einstein",
|
||||
UidNumber: 123,
|
||||
GidNumber: 987,
|
||||
},
|
||||
"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c": {
|
||||
Id: &userpb.UserId{
|
||||
Idp: "http://localhost:9998",
|
||||
OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c",
|
||||
Type: userpb.UserType_USER_TYPE_PRIMARY,
|
||||
},
|
||||
Username: "marie",
|
||||
Groups: []string{"radium-lovers", "polonium-lovers", "physics-lovers"},
|
||||
Mail: "marie@example.org",
|
||||
DisplayName: "Marie Curie",
|
||||
UidNumber: 456,
|
||||
GidNumber: 987,
|
||||
},
|
||||
"932b4540-8d16-481e-8ef4-588e4b6b151c": {
|
||||
Id: &userpb.UserId{
|
||||
Idp: "http://localhost:9998",
|
||||
OpaqueId: "932b4540-8d16-481e-8ef4-588e4b6b151c",
|
||||
Type: userpb.UserType_USER_TYPE_PRIMARY,
|
||||
},
|
||||
Username: "richard",
|
||||
Groups: []string{"quantum-lovers", "philosophy-haters", "physics-lovers"},
|
||||
Mail: "richard@example.org",
|
||||
DisplayName: "Richard Feynman",
|
||||
},
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
// 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 json
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/user"
|
||||
"github.com/cs3org/reva/v2/pkg/user/manager/registry"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/errtypes"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Register("json", New)
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
users []*userpb.User
|
||||
}
|
||||
|
||||
type config struct {
|
||||
// Users holds a path to a file containing json conforming to the Users struct
|
||||
Users string `mapstructure:"users"`
|
||||
}
|
||||
|
||||
func (c *config) init() {
|
||||
if c.Users == "" {
|
||||
c.Users = "/etc/revad/users.json"
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
c.init()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// New returns a user manager implementation that reads a json file to provide user metadata.
|
||||
func New(m map[string]interface{}) (user.Manager, error) {
|
||||
mgr := &manager{}
|
||||
err := mgr.Configure(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mgr, nil
|
||||
}
|
||||
|
||||
func (m *manager) Configure(ml map[string]interface{}) error {
|
||||
c, err := parseConfig(ml)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.ReadFile(c.Users)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
users := []*userpb.User{}
|
||||
|
||||
err = json.Unmarshal(f, &users)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.users = users
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
for _, u := range m.users {
|
||||
if (u.Id.GetOpaqueId() == uid.OpaqueId || u.Username == uid.OpaqueId) && (uid.Idp == "" || uid.Idp == u.Id.GetIdp()) {
|
||||
user := *u
|
||||
if skipFetchingGroups {
|
||||
user.Groups = nil
|
||||
}
|
||||
return &user, 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.users {
|
||||
if userClaim, err := extractClaim(u, claim); err == nil && value == userClaim {
|
||||
user := *u
|
||||
if skipFetchingGroups {
|
||||
user.Groups = nil
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
}
|
||||
return nil, errtypes.NotFound(value)
|
||||
}
|
||||
|
||||
func extractClaim(u *userpb.User, claim string) (string, error) {
|
||||
switch claim {
|
||||
case "mail":
|
||||
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
|
||||
}
|
||||
}
|
||||
return "", errors.New("json: invalid field")
|
||||
}
|
||||
|
||||
// TODO(jfd) search Opaque? compare sub?
|
||||
func userContains(u *userpb.User, query string) bool {
|
||||
query = strings.ToLower(query)
|
||||
return strings.Contains(strings.ToLower(u.Username), query) || strings.Contains(strings.ToLower(u.DisplayName), query) ||
|
||||
strings.Contains(strings.ToLower(u.Mail), query) || strings.Contains(strings.ToLower(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.users {
|
||||
if userContains(u, query) {
|
||||
user := *u
|
||||
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
|
||||
}
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
// 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 ldap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/appctx"
|
||||
"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/cs3org/reva/v2/pkg/utils"
|
||||
ldapIdentity "github.com/cs3org/reva/v2/pkg/utils/ldap"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/google/uuid"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Register("ldap", New)
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
c *config
|
||||
ldapClient ldap.Client
|
||||
}
|
||||
|
||||
type config struct {
|
||||
utils.LDAPConn `mapstructure:",squash"`
|
||||
LDAPIdentity ldapIdentity.Identity `mapstructure:",squash"`
|
||||
Idp string `mapstructure:"idp"`
|
||||
// Nobody specifies the fallback uid number for users that don't have a uidNumber set in LDAP
|
||||
Nobody int64 `mapstructure:"nobody"`
|
||||
}
|
||||
|
||||
func parseConfig(m map[string]interface{}) (*config, error) {
|
||||
c := config{
|
||||
LDAPIdentity: ldapIdentity.New(),
|
||||
}
|
||||
if err := mapstructure.Decode(m, &c); err != nil {
|
||||
err = errors.Wrap(err, "error decoding conf")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// New returns a user manager implementation that connects to a LDAP server to provide user metadata.
|
||||
func New(m map[string]interface{}) (user.Manager, error) {
|
||||
mgr := &manager{}
|
||||
err := mgr.Configure(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mgr.ldapClient, err = utils.GetLDAPClientWithReconnect(&mgr.c.LDAPConn)
|
||||
return mgr, err
|
||||
}
|
||||
|
||||
// Configure initializes the configuration of the user manager from the supplied config map
|
||||
func (m *manager) Configure(ml map[string]interface{}) error {
|
||||
c, err := parseConfig(ml)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Nobody == 0 {
|
||||
c.Nobody = 99
|
||||
}
|
||||
|
||||
if err = c.LDAPIdentity.Setup(); err != nil {
|
||||
return fmt.Errorf("error setting up Identity config: %w", err)
|
||||
}
|
||||
m.c = c
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUser implements the user.Manager interface. Looks up a user by Id and return the user
|
||||
func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
log := appctx.GetLogger(ctx)
|
||||
|
||||
log.Debug().Interface("id", uid).Msg("GetUser")
|
||||
// If the Idp value in the uid does not match our config, we can't answer this request
|
||||
if uid.Idp != "" && uid.Idp != m.c.Idp {
|
||||
return nil, errtypes.NotFound("idp mismatch")
|
||||
}
|
||||
|
||||
userEntry, err := m.c.LDAPIdentity.GetLDAPUserByID(log, m.ldapClient, uid.OpaqueId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug().Interface("entry", userEntry).Msg("entries")
|
||||
|
||||
u, err := m.ldapEntryToUser(userEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if skipFetchingGroups {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, userEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.Groups = groups
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// GetUserByClaim implements the user.Manager interface. Looks up a user by
|
||||
// claim ('mail', 'username', 'userid') and returns the user.
|
||||
func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
log := appctx.GetLogger(ctx)
|
||||
|
||||
log.Debug().Str("claim", claim).Str("value", value).Msg("GetUserByClaim")
|
||||
userEntry, err := m.c.LDAPIdentity.GetLDAPUserByAttribute(log, m.ldapClient, claim, value)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("GetUserByClaim")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug().Interface("entry", userEntry).Msg("entries")
|
||||
|
||||
u, err := m.ldapEntryToUser(userEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if m.c.LDAPIdentity.IsLDAPUserInDisabledGroup(log, m.ldapClient, userEntry) {
|
||||
return nil, errtypes.NotFound("user is locally disabled")
|
||||
}
|
||||
|
||||
if skipFetchingGroups {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, userEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.Groups = groups
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// FindUser implements the user.Manager interface. Searches for users using a prefix-substring search on
|
||||
// the user attributes ('mail', 'username', 'displayname', 'userid') and returns the users.
|
||||
func (m *manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) {
|
||||
log := appctx.GetLogger(ctx)
|
||||
entries, err := m.c.LDAPIdentity.GetLDAPUsers(log, m.ldapClient, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users := []*userpb.User{}
|
||||
|
||||
for _, entry := range entries {
|
||||
u, err := m.ldapEntryToUser(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !skipFetchingGroups {
|
||||
groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Groups = groups
|
||||
}
|
||||
|
||||
users = append(users, u)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// GetUserGroups implements the user.Manager interface. Looks up all group membership of
|
||||
// the user with the supplied Id. Returns a string slice with the group ids
|
||||
func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) {
|
||||
log := appctx.GetLogger(ctx)
|
||||
if uid.Idp != "" && uid.Idp != m.c.Idp {
|
||||
log.Debug().Str("useridp", uid.Idp).Str("configured idp", m.c.Idp).Msg("IDP mismatch")
|
||||
return nil, errtypes.NotFound("idp mismatch")
|
||||
}
|
||||
userEntry, err := m.c.LDAPIdentity.GetLDAPUserByID(log, m.ldapClient, uid.OpaqueId)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Interface("userid", uid).Msg("Failed to lookup user")
|
||||
return []string{}, err
|
||||
}
|
||||
return m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, userEntry)
|
||||
}
|
||||
|
||||
func (m *manager) ldapEntryToUser(entry *ldap.Entry) (*userpb.User, error) {
|
||||
id, err := m.ldapEntryToUserID(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gidNumber := m.c.Nobody
|
||||
gidValue := entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.GIDNumber)
|
||||
if gidValue != "" {
|
||||
gidNumber, err = strconv.ParseInt(gidValue, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
uidNumber := m.c.Nobody
|
||||
uidValue := entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.UIDNumber)
|
||||
if uidValue != "" {
|
||||
uidNumber, err = strconv.ParseInt(uidValue, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
u := &userpb.User{
|
||||
Id: id,
|
||||
Username: entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.Username),
|
||||
Mail: entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.Mail),
|
||||
DisplayName: entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.DisplayName),
|
||||
GidNumber: gidNumber,
|
||||
UidNumber: uidNumber,
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (m *manager) ldapEntryToUserID(entry *ldap.Entry) (*userpb.UserId, error) {
|
||||
var uid string
|
||||
if m.c.LDAPIdentity.User.Schema.IDIsOctetString {
|
||||
rawValue := entry.GetEqualFoldRawAttributeValue(m.c.LDAPIdentity.User.Schema.ID)
|
||||
if value, err := uuid.FromBytes(rawValue); err == nil {
|
||||
uid = value.String()
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
uid = entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.ID)
|
||||
}
|
||||
|
||||
return &userpb.UserId{
|
||||
Idp: m.c.Idp,
|
||||
OpaqueId: uid,
|
||||
Type: m.c.LDAPIdentity.GetUserType(entry),
|
||||
}, nil
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// 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 loader
|
||||
|
||||
import (
|
||||
// Load core user manager drivers.
|
||||
_ "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
|
||||
)
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
// 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 "userid":
|
||||
return u.ID.OpaqueId, 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
|
||||
}
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
// 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 nextcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
|
||||
"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"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
// "github.com/cs3org/reva/v2/pkg/errtypes"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Register("nextcloud", New)
|
||||
}
|
||||
|
||||
// Manager is the Nextcloud-based implementation of the share.Manager interface
|
||||
// see https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
|
||||
type Manager struct {
|
||||
client *http.Client
|
||||
sharedSecret string
|
||||
endPoint string
|
||||
}
|
||||
|
||||
// UserManagerConfig contains config for a Nextcloud-based UserManager
|
||||
type UserManagerConfig struct {
|
||||
EndPoint string `mapstructure:"endpoint" docs:";The Nextcloud backend endpoint for user management"`
|
||||
SharedSecret string `mapstructure:"shared_secret"`
|
||||
MockHTTP bool `mapstructure:"mock_http"`
|
||||
}
|
||||
|
||||
func (c *UserManagerConfig) init() {
|
||||
if c.EndPoint == "" {
|
||||
c.EndPoint = "http://localhost/end/point?"
|
||||
}
|
||||
}
|
||||
|
||||
func parseConfig(m map[string]interface{}) (*UserManagerConfig, error) {
|
||||
c := &UserManagerConfig{}
|
||||
if err := mapstructure.Decode(m, c); err != nil {
|
||||
err = errors.Wrap(err, "error decoding conf")
|
||||
return nil, err
|
||||
}
|
||||
c.init()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Action describes a REST request to forward to the Nextcloud backend
|
||||
type Action struct {
|
||||
verb string
|
||||
argS string
|
||||
}
|
||||
|
||||
// New returns a user manager implementation that reads a json file to provide user metadata.
|
||||
func New(m map[string]interface{}) (user.Manager, error) {
|
||||
c, err := parseConfig(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.init()
|
||||
|
||||
return NewUserManager(c)
|
||||
}
|
||||
|
||||
// NewUserManager returns a new Nextcloud-based UserManager
|
||||
func NewUserManager(c *UserManagerConfig) (*Manager, error) {
|
||||
var client *http.Client
|
||||
if c.MockHTTP {
|
||||
// Wait for SetHTTPClient to be called later
|
||||
client = nil
|
||||
} else {
|
||||
if len(c.EndPoint) == 0 {
|
||||
return nil, errors.New("Please specify 'endpoint' in '[grpc.services.userprovider.drivers.nextcloud]'")
|
||||
}
|
||||
client = &http.Client{}
|
||||
}
|
||||
|
||||
return &Manager{
|
||||
endPoint: c.EndPoint, // e.g. "http://nc/apps/sciencemesh/"
|
||||
sharedSecret: c.SharedSecret,
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetHTTPClient sets the HTTP client
|
||||
func (um *Manager) SetHTTPClient(c *http.Client) {
|
||||
um.client = c
|
||||
}
|
||||
|
||||
func getUser(ctx context.Context) (*userpb.User, error) {
|
||||
u, ok := ctxpkg.ContextGetUser(ctx)
|
||||
if !ok {
|
||||
err := errors.Wrap(errtypes.UserRequired(""), "nextcloud storage driver: error getting user from ctx")
|
||||
return nil, err
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (um *Manager) do(ctx context.Context, a Action, username string) (int, []byte, error) {
|
||||
url := um.endPoint + "~" + username + "/api/user/" + a.verb
|
||||
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(a.argS))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
req.Header.Set("X-Reva-Secret", um.sharedSecret)
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
fmt.Println(url)
|
||||
resp, err := um.client.Do(req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
return resp.StatusCode, body, err
|
||||
}
|
||||
|
||||
// Configure method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
|
||||
func (um *Manager) Configure(ml map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUser method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
|
||||
func (um *Manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
bodyStr, _ := json.Marshal(uid)
|
||||
_, respBody, err := um.do(ctx, Action{"GetUser", string(bodyStr)}, "unauthenticated")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &userpb.User{}
|
||||
err = json.Unmarshal(respBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// GetUserByClaim method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
|
||||
func (um *Manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
type paramsObj struct {
|
||||
Claim string `json:"claim"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
bodyObj := ¶msObj{
|
||||
Claim: claim,
|
||||
Value: value,
|
||||
}
|
||||
user, err := getUser(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bodyStr, _ := json.Marshal(bodyObj)
|
||||
_, respBody, err := um.do(ctx, Action{"GetUserByClaim", string(bodyStr)}, user.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &userpb.User{}
|
||||
err = json.Unmarshal(respBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// GetUserGroups method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
|
||||
func (um *Manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) {
|
||||
bodyStr, err := json.Marshal(uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := getUser(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, respBody, err := um.do(ctx, Action{"GetUserGroups", string(bodyStr)}, user.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var gs []string
|
||||
err = json.Unmarshal(respBody, &gs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gs, err
|
||||
}
|
||||
|
||||
// FindUsers method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
|
||||
func (um *Manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) {
|
||||
user, err := getUser(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, respBody, err := um.do(ctx, Action{"FindUsers", query}, user.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var respArr []userpb.User
|
||||
err = json.Unmarshal(respBody, &respArr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pointers = make([]*userpb.User, len(respArr))
|
||||
for i := 0; i < len(respArr); i++ {
|
||||
pointers[i] = &respArr[i]
|
||||
}
|
||||
return pointers, err
|
||||
}
|
||||
Generated
Vendored
+103
@@ -0,0 +1,103 @@
|
||||
// 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 nextcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Response contains data for the Nextcloud mock server to respond
|
||||
// and to switch to a new server state
|
||||
type Response struct {
|
||||
code int
|
||||
body string
|
||||
newServerState string
|
||||
}
|
||||
|
||||
const serverStateError = "ERROR"
|
||||
const serverStateEmpty = "EMPTY"
|
||||
const serverStateHome = "HOME"
|
||||
|
||||
var serverState = serverStateEmpty
|
||||
|
||||
var responses = map[string]Response{
|
||||
`POST /apps/sciencemesh/~unauthenticated/api/user/GetUser {"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}`: {200, `{"id":{"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}}`, serverStateHome},
|
||||
`POST /apps/sciencemesh/~tester/api/user/GetUserByClaim {"claim":"claim-string","value":"value-string"}`: {200, `{"id":{"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}}`, serverStateHome},
|
||||
`POST /apps/sciencemesh/~tester/api/user/GetUserGroups {"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}`: {200, `["wine-lovers"]`, serverStateHome},
|
||||
`POST /apps/sciencemesh/~tester/api/user/FindUsers some-query`: {200, `[{"id":{"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}}]`, serverStateHome},
|
||||
}
|
||||
|
||||
// GetNextcloudServerMock returns a handler that pretends to be a remote Nextcloud server
|
||||
func GetNextcloudServerMock(called *[]string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
buf := new(strings.Builder)
|
||||
_, err := io.Copy(buf, r.Body)
|
||||
if err != nil {
|
||||
panic("Error reading response into buffer")
|
||||
}
|
||||
var key = fmt.Sprintf("%s %s %s", r.Method, r.URL, buf.String())
|
||||
*called = append(*called, key)
|
||||
response := responses[key]
|
||||
if (response == Response{}) {
|
||||
key = fmt.Sprintf("%s %s %s %s", r.Method, r.URL, buf.String(), serverState)
|
||||
// *called = append(*called, key)
|
||||
response = responses[key]
|
||||
}
|
||||
if (response == Response{}) {
|
||||
fmt.Printf("%s %s %s %s", r.Method, r.URL, buf.String(), serverState)
|
||||
response = Response{500, fmt.Sprintf("response not defined! %s", key), serverStateEmpty}
|
||||
}
|
||||
serverState = responses[key].newServerState
|
||||
if serverState == `` {
|
||||
serverState = serverStateError
|
||||
}
|
||||
w.WriteHeader(response.code)
|
||||
// w.Header().Set("Etag", "mocker-etag")
|
||||
_, err = w.Write([]byte(responses[key].body))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestingHTTPClient thanks to https://itnext.io/how-to-stub-requests-to-remote-hosts-with-go-6c2c1db32bf2
|
||||
// Ideally, this function would live in tests/helpers, but
|
||||
// if we put it there, it gets excluded by .dockerignore, and the
|
||||
// Docker build fails (see https://github.com/cs3org/reva/issues/1999)
|
||||
// So putting it here for now - open to suggestions if someone knows
|
||||
// a better way to inject this.
|
||||
func TestingHTTPClient(handler http.Handler) (*http.Client, func()) {
|
||||
s := httptest.NewServer(handler)
|
||||
|
||||
cli := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(_ context.Context, network, _ string) (net.Conn, error) {
|
||||
return net.Dial(network, s.Listener.Addr().String())
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return cli, s.Close
|
||||
}
|
||||
Generated
Vendored
+228
@@ -0,0 +1,228 @@
|
||||
// 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 accounts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cs3org/reva/v2/pkg/appctx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Accounts represents oc10-style Accounts
|
||||
type Accounts struct {
|
||||
driver string
|
||||
db *sql.DB
|
||||
joinUsername, joinUUID, enableMedialSearch bool
|
||||
selectSQL string
|
||||
}
|
||||
|
||||
// NewMysql returns a new Cache instance connecting to a MySQL database
|
||||
func NewMysql(dsn string, joinUsername, joinUUID, enableMedialSearch bool) (*Accounts, error) {
|
||||
sqldb, err := sql.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error connecting to the database")
|
||||
}
|
||||
|
||||
// FIXME make configurable
|
||||
sqldb.SetConnMaxLifetime(time.Minute * 3)
|
||||
sqldb.SetConnMaxIdleTime(time.Second * 30)
|
||||
sqldb.SetMaxOpenConns(100)
|
||||
sqldb.SetMaxIdleConns(10)
|
||||
|
||||
err = sqldb.Ping()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error connecting to the database")
|
||||
}
|
||||
|
||||
return New("mysql", sqldb, joinUsername, joinUUID, enableMedialSearch)
|
||||
}
|
||||
|
||||
// New returns a new Cache instance connecting to the given sql.DB
|
||||
func New(driver string, sqldb *sql.DB, joinUsername, joinUUID, enableMedialSearch bool) (*Accounts, error) {
|
||||
|
||||
sel := "SELECT id, email, user_id, display_name, quota, last_login, backend, home, state"
|
||||
from := `
|
||||
FROM oc_accounts a
|
||||
`
|
||||
if joinUsername {
|
||||
sel += ", p.configvalue AS username"
|
||||
from += `LEFT JOIN oc_preferences p
|
||||
ON a.user_id=p.userid
|
||||
AND p.appid='core'
|
||||
AND p.configkey='username'`
|
||||
} else {
|
||||
// fallback to user_id as username
|
||||
sel += ", user_id AS username"
|
||||
}
|
||||
if joinUUID {
|
||||
sel += ", p2.configvalue AS ownclouduuid"
|
||||
from += `LEFT JOIN oc_preferences p2
|
||||
ON a.user_id=p2.userid
|
||||
AND p2.appid='core'
|
||||
AND p2.configkey='ownclouduuid'`
|
||||
} else {
|
||||
// fallback to user_id as ownclouduuid
|
||||
sel += ", user_id AS ownclouduuid"
|
||||
}
|
||||
|
||||
return &Accounts{
|
||||
driver: driver,
|
||||
db: sqldb,
|
||||
joinUsername: joinUsername,
|
||||
joinUUID: joinUUID,
|
||||
enableMedialSearch: enableMedialSearch,
|
||||
selectSQL: sel + from,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Account stores information about accounts.
|
||||
type Account struct {
|
||||
ID uint64
|
||||
Email sql.NullString
|
||||
UserID string
|
||||
DisplayName sql.NullString
|
||||
Quota sql.NullString
|
||||
LastLogin int
|
||||
Backend string
|
||||
Home string
|
||||
State int8
|
||||
Username sql.NullString // optional comes from the oc_preferences
|
||||
OwnCloudUUID sql.NullString // optional comes from the oc_preferences
|
||||
}
|
||||
|
||||
func (as *Accounts) rowToAccount(ctx context.Context, row Scannable) (*Account, error) {
|
||||
a := Account{}
|
||||
if err := row.Scan(&a.ID, &a.Email, &a.UserID, &a.DisplayName, &a.Quota, &a.LastLogin, &a.Backend, &a.Home, &a.State, &a.Username, &a.OwnCloudUUID); err != nil {
|
||||
appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
// Scannable describes the interface providing a Scan method
|
||||
type Scannable interface {
|
||||
Scan(...interface{}) error
|
||||
}
|
||||
|
||||
// GetAccountByClaim fetches an account by mail, username or userid
|
||||
func (as *Accounts) GetAccountByClaim(ctx context.Context, claim, value string) (*Account, error) {
|
||||
// TODO align supported claims with rest driver and the others, maybe refactor into common mapping
|
||||
var row *sql.Row
|
||||
var where string
|
||||
switch claim {
|
||||
case "mail":
|
||||
where = "WHERE a.email=?"
|
||||
// case "uid":
|
||||
// claim = m.c.Schema.UIDNumber
|
||||
// case "gid":
|
||||
// claim = m.c.Schema.GIDNumber
|
||||
case "username":
|
||||
if as.joinUsername {
|
||||
where = "WHERE p.configvalue=?"
|
||||
} else {
|
||||
// use user_id as username
|
||||
where = "WHERE a.user_id=?"
|
||||
}
|
||||
case "userid":
|
||||
if as.joinUUID {
|
||||
where = "WHERE p2.configvalue=?"
|
||||
} else {
|
||||
// use user_id as uuid
|
||||
where = "WHERE a.user_id=?"
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("owncloudsql: invalid field " + claim)
|
||||
}
|
||||
|
||||
row = as.db.QueryRowContext(ctx, as.selectSQL+where, value)
|
||||
|
||||
return as.rowToAccount(ctx, row)
|
||||
}
|
||||
|
||||
func sanitizeWildcards(q string) string {
|
||||
return strings.ReplaceAll(strings.ReplaceAll(q, "%", `\%`), "_", `\_`)
|
||||
}
|
||||
|
||||
// FindAccounts searches userid, displayname and email using the given query. The Wildcard caracters % and _ are escaped.
|
||||
func (as *Accounts) FindAccounts(ctx context.Context, query string) ([]Account, error) {
|
||||
if as.enableMedialSearch {
|
||||
query = "%" + sanitizeWildcards(query) + "%"
|
||||
}
|
||||
// TODO join oc_account_terms
|
||||
where := "WHERE a.user_id LIKE ? OR a.display_name LIKE ? OR a.email LIKE ?"
|
||||
args := []interface{}{query, query, query}
|
||||
|
||||
if as.joinUsername {
|
||||
where += " OR p.configvalue LIKE ?"
|
||||
args = append(args, query)
|
||||
}
|
||||
if as.joinUUID {
|
||||
where += " OR p2.configvalue LIKE ?"
|
||||
args = append(args, query)
|
||||
}
|
||||
|
||||
rows, err := as.db.QueryContext(ctx, as.selectSQL+where, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
accounts := []Account{}
|
||||
for rows.Next() {
|
||||
a := Account{}
|
||||
if err := rows.Scan(&a.ID, &a.Email, &a.UserID, &a.DisplayName, &a.Quota, &a.LastLogin, &a.Backend, &a.Home, &a.State, &a.Username, &a.OwnCloudUUID); err != nil {
|
||||
appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
|
||||
continue
|
||||
}
|
||||
accounts = append(accounts, a)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
// GetAccountGroups lasts the groups for an account
|
||||
func (as *Accounts) GetAccountGroups(ctx context.Context, uid string) ([]string, error) {
|
||||
rows, err := as.db.QueryContext(ctx, "SELECT gid FROM oc_group_user WHERE uid=?", uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
groups := []string{}
|
||||
for rows.Next() {
|
||||
var group string
|
||||
if err := rows.Scan(&group); err != nil {
|
||||
appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
|
||||
continue
|
||||
}
|
||||
groups = append(groups, group)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
Generated
Vendored
BIN
Binary file not shown.
+184
@@ -0,0 +1,184 @@
|
||||
// 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 owncloudsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/appctx"
|
||||
"github.com/cs3org/reva/v2/pkg/errtypes"
|
||||
"github.com/cs3org/reva/v2/pkg/user"
|
||||
"github.com/cs3org/reva/v2/pkg/user/manager/owncloudsql/accounts"
|
||||
"github.com/cs3org/reva/v2/pkg/user/manager/registry"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
// Provides mysql drivers
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Register("owncloudsql", NewMysql)
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
c *config
|
||||
db *accounts.Accounts
|
||||
}
|
||||
|
||||
type config struct {
|
||||
DbUsername string `mapstructure:"dbusername"`
|
||||
DbPassword string `mapstructure:"dbpassword"`
|
||||
DbHost string `mapstructure:"dbhost"`
|
||||
DbPort int `mapstructure:"dbport"`
|
||||
DbName string `mapstructure:"dbname"`
|
||||
Idp string `mapstructure:"idp"`
|
||||
Nobody int64 `mapstructure:"nobody"`
|
||||
JoinUsername bool `mapstructure:"join_username"`
|
||||
JoinOwnCloudUUID bool `mapstructure:"join_ownclouduuid"`
|
||||
EnableMedialSearch bool `mapstructure:"enable_medial_search"`
|
||||
}
|
||||
|
||||
// NewMysql returns a new user manager connection to an owncloud mysql database
|
||||
func NewMysql(m map[string]interface{}) (user.Manager, error) {
|
||||
mgr := &manager{}
|
||||
err := mgr.Configure(m)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error creating a new manager")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mgr.db, err = accounts.NewMysql(
|
||||
fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", mgr.c.DbUsername, mgr.c.DbPassword, mgr.c.DbHost, mgr.c.DbPort, mgr.c.DbName),
|
||||
mgr.c.JoinUsername,
|
||||
mgr.c.JoinOwnCloudUUID,
|
||||
mgr.c.EnableMedialSearch,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mgr, nil
|
||||
}
|
||||
|
||||
func (m *manager) Configure(ml map[string]interface{}) error {
|
||||
c, err := parseConfig(ml)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Nobody == 0 {
|
||||
c.Nobody = 99
|
||||
}
|
||||
|
||||
m.c = c
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseConfig(m map[string]interface{}) (*config, error) {
|
||||
c := &config{}
|
||||
if err := mapstructure.Decode(m, &c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
// search via the user_id
|
||||
a, err := m.db.GetAccountByClaim(ctx, "userid", uid.OpaqueId)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound(uid.OpaqueId)
|
||||
}
|
||||
return m.convertToCS3User(ctx, a, skipFetchingGroups)
|
||||
}
|
||||
|
||||
func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
a, err := m.db.GetAccountByClaim(ctx, claim, value)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound(claim + "=" + value)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m.convertToCS3User(ctx, a, skipFetchingGroups)
|
||||
}
|
||||
|
||||
func (m *manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) {
|
||||
|
||||
accounts, err := m.db.FindAccounts(ctx, query)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound("no users found for " + query)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users := make([]*userpb.User, 0, len(accounts))
|
||||
for i := range accounts {
|
||||
u, err := m.convertToCS3User(ctx, &accounts[i], skipFetchingGroups)
|
||||
if err != nil {
|
||||
appctx.GetLogger(ctx).Error().Err(err).Interface("account", accounts[i]).Msg("could not convert account, skipping")
|
||||
continue
|
||||
}
|
||||
users = append(users, u)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) {
|
||||
groups, err := m.db.GetAccountGroups(ctx, uid.OpaqueId)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errtypes.NotFound("no groups found for uid " + uid.OpaqueId)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (m *manager) convertToCS3User(ctx context.Context, a *accounts.Account, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
u := &userpb.User{
|
||||
Id: &userpb.UserId{
|
||||
Idp: m.c.Idp,
|
||||
OpaqueId: a.OwnCloudUUID.String,
|
||||
Type: userpb.UserType_USER_TYPE_PRIMARY,
|
||||
},
|
||||
Username: a.Username.String,
|
||||
Mail: a.Email.String,
|
||||
DisplayName: a.DisplayName.String,
|
||||
//Groups: groups,
|
||||
GidNumber: m.c.Nobody,
|
||||
UidNumber: m.c.Nobody,
|
||||
}
|
||||
if u.Username == "" {
|
||||
u.Username = u.Id.OpaqueId
|
||||
}
|
||||
if u.DisplayName == "" {
|
||||
u.DisplayName = u.Id.OpaqueId
|
||||
}
|
||||
|
||||
if !skipFetchingGroups {
|
||||
var err error
|
||||
if u.Groups, err = m.GetUserGroups(ctx, u.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// 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 registry
|
||||
|
||||
import (
|
||||
"github.com/cs3org/reva/v2/pkg/user"
|
||||
)
|
||||
|
||||
// NewFunc is the function that user managers
|
||||
// should register at init time.
|
||||
type NewFunc func(map[string]interface{}) (user.Manager, error)
|
||||
|
||||
// NewFuncs is a map containing all the registered user managers.
|
||||
var NewFuncs = map[string]NewFunc{}
|
||||
|
||||
// Register registers a new user manager new function.
|
||||
// Not safe for concurrent use. Safe for use from package init.
|
||||
func Register(name string, f NewFunc) {
|
||||
NewFuncs[name] = f
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
// 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 user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"net/rpc"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/appctx"
|
||||
"github.com/cs3org/reva/v2/pkg/plugin"
|
||||
hcplugin "github.com/hashicorp/go-plugin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(&userpb.User{})
|
||||
plugin.Register("userprovider", &ProviderPlugin{})
|
||||
}
|
||||
|
||||
// ProviderPlugin is the implementation of plugin.Plugin so we can serve/consume this.
|
||||
type ProviderPlugin struct {
|
||||
Impl Manager
|
||||
}
|
||||
|
||||
// Server returns the RPC Server which serves the methods that the Client calls over net/rpc
|
||||
func (p *ProviderPlugin) Server(*hcplugin.MuxBroker) (interface{}, error) {
|
||||
return &RPCServer{Impl: p.Impl}, nil
|
||||
}
|
||||
|
||||
// Client returns interface implementation for the plugin that communicates to the server end of the plugin
|
||||
func (p *ProviderPlugin) Client(b *hcplugin.MuxBroker, c *rpc.Client) (interface{}, error) {
|
||||
return &RPCClient{Client: c}, nil
|
||||
}
|
||||
|
||||
// RPCClient is an implementation of Manager that talks over RPC.
|
||||
type RPCClient struct{ Client *rpc.Client }
|
||||
|
||||
// ConfigureArg for RPC
|
||||
type ConfigureArg struct {
|
||||
Ml map[string]interface{}
|
||||
}
|
||||
|
||||
// ConfigureReply for RPC
|
||||
type ConfigureReply struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Configure RPCClient configure method
|
||||
func (m *RPCClient) Configure(ml map[string]interface{}) error {
|
||||
args := ConfigureArg{Ml: ml}
|
||||
resp := ConfigureReply{}
|
||||
err := m.Client.Call("Plugin.Configure", args, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return resp.Err
|
||||
}
|
||||
|
||||
// GetUserArg for RPC
|
||||
type GetUserArg struct {
|
||||
Ctx map[interface{}]interface{}
|
||||
UID *userpb.UserId
|
||||
SkipFetchingGroups bool
|
||||
}
|
||||
|
||||
// GetUserReply for RPC
|
||||
type GetUserReply struct {
|
||||
User *userpb.User
|
||||
Err error
|
||||
}
|
||||
|
||||
// GetUser RPCClient GetUser method
|
||||
func (m *RPCClient) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
ctxVal := appctx.GetKeyValuesFromCtx(ctx)
|
||||
args := GetUserArg{Ctx: ctxVal, UID: uid, SkipFetchingGroups: skipFetchingGroups}
|
||||
resp := GetUserReply{}
|
||||
err := m.Client.Call("Plugin.GetUser", args, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.User, resp.Err
|
||||
}
|
||||
|
||||
// GetUserByClaimArg for RPC
|
||||
type GetUserByClaimArg struct {
|
||||
Ctx map[interface{}]interface{}
|
||||
Claim string
|
||||
Value string
|
||||
SkipFetchingGroups bool
|
||||
}
|
||||
|
||||
// GetUserByClaimReply for RPC
|
||||
type GetUserByClaimReply struct {
|
||||
User *userpb.User
|
||||
Err error
|
||||
}
|
||||
|
||||
// GetUserByClaim RPCClient GetUserByClaim method
|
||||
func (m *RPCClient) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
|
||||
ctxVal := appctx.GetKeyValuesFromCtx(ctx)
|
||||
args := GetUserByClaimArg{Ctx: ctxVal, Claim: claim, Value: value, SkipFetchingGroups: skipFetchingGroups}
|
||||
resp := GetUserByClaimReply{}
|
||||
err := m.Client.Call("Plugin.GetUserByClaim", args, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.User, resp.Err
|
||||
}
|
||||
|
||||
// GetUserGroupsArg for RPC
|
||||
type GetUserGroupsArg struct {
|
||||
Ctx map[interface{}]interface{}
|
||||
User *userpb.UserId
|
||||
}
|
||||
|
||||
// GetUserGroupsReply for RPC
|
||||
type GetUserGroupsReply struct {
|
||||
Group []string
|
||||
Err error
|
||||
}
|
||||
|
||||
// GetUserGroups RPCClient GetUserGroups method
|
||||
func (m *RPCClient) GetUserGroups(ctx context.Context, user *userpb.UserId) ([]string, error) {
|
||||
ctxVal := appctx.GetKeyValuesFromCtx(ctx)
|
||||
args := GetUserGroupsArg{Ctx: ctxVal, User: user}
|
||||
resp := GetUserGroupsReply{}
|
||||
err := m.Client.Call("Plugin.GetUserGroups", args, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Group, resp.Err
|
||||
}
|
||||
|
||||
// FindUsersArg for RPC
|
||||
type FindUsersArg struct {
|
||||
Ctx map[interface{}]interface{}
|
||||
Query string
|
||||
SkipFetchingGroups bool
|
||||
}
|
||||
|
||||
// FindUsersReply for RPC
|
||||
type FindUsersReply struct {
|
||||
User []*userpb.User
|
||||
Err error
|
||||
}
|
||||
|
||||
// FindUsers RPCClient FindUsers method
|
||||
func (m *RPCClient) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) {
|
||||
ctxVal := appctx.GetKeyValuesFromCtx(ctx)
|
||||
args := FindUsersArg{Ctx: ctxVal, Query: query, SkipFetchingGroups: skipFetchingGroups}
|
||||
resp := FindUsersReply{}
|
||||
err := m.Client.Call("Plugin.FindUsers", args, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.User, resp.Err
|
||||
}
|
||||
|
||||
// RPCServer is the server that RPCClient talks to, conforming to the requirements of net/rpc
|
||||
type RPCServer struct {
|
||||
// This is the real implementation
|
||||
Impl Manager
|
||||
}
|
||||
|
||||
// Configure RPCServer Configure method
|
||||
func (m *RPCServer) Configure(args ConfigureArg, resp *ConfigureReply) error {
|
||||
resp.Err = m.Impl.Configure(args.Ml)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUser RPCServer GetUser method
|
||||
func (m *RPCServer) GetUser(args GetUserArg, resp *GetUserReply) error {
|
||||
ctx := appctx.PutKeyValuesToCtx(args.Ctx)
|
||||
resp.User, resp.Err = m.Impl.GetUser(ctx, args.UID, args.SkipFetchingGroups)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserByClaim RPCServer GetUserByClaim method
|
||||
func (m *RPCServer) GetUserByClaim(args GetUserByClaimArg, resp *GetUserByClaimReply) error {
|
||||
ctx := appctx.PutKeyValuesToCtx(args.Ctx)
|
||||
resp.User, resp.Err = m.Impl.GetUserByClaim(ctx, args.Claim, args.Value, args.SkipFetchingGroups)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserGroups RPCServer GetUserGroups method
|
||||
func (m *RPCServer) GetUserGroups(args GetUserGroupsArg, resp *GetUserGroupsReply) error {
|
||||
ctx := appctx.PutKeyValuesToCtx(args.Ctx)
|
||||
resp.Group, resp.Err = m.Impl.GetUserGroups(ctx, args.User)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindUsers RPCServer FindUsers method
|
||||
func (m *RPCServer) FindUsers(args FindUsersArg, resp *FindUsersReply) error {
|
||||
ctx := appctx.PutKeyValuesToCtx(args.Ctx)
|
||||
resp.User, resp.Err = m.Impl.FindUsers(ctx, args.Query, args.SkipFetchingGroups)
|
||||
return nil
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// 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 user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/plugin"
|
||||
)
|
||||
|
||||
// Manager is the interface to implement to manipulate users.
|
||||
type Manager interface {
|
||||
plugin.Plugin
|
||||
// GetUser returns the user metadata identified by a uid.
|
||||
// The groups of the user are omitted if specified, as these might not be required for certain operations
|
||||
// and might involve computational overhead.
|
||||
GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error)
|
||||
// GetUserByClaim returns the user identified by a specific value for a given claim.
|
||||
GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error)
|
||||
// GetUserGroups returns the groups a user identified by a uid belongs to.
|
||||
GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error)
|
||||
// FindUsers returns all the user objects which match a query parameter.
|
||||
FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error)
|
||||
}
|
||||
Reference in New Issue
Block a user