mirror of
https://github.com/owncloud/ocis
synced 2026-04-25 17:25:21 +02:00
Merge pull request #11695 from owncloud/feat/user-search-attributes
feat: [OCISDEV-234] add configurable display attributes
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
Enhancement: Add configurable display attributes
|
||||
|
||||
We added new configuration options `UserSearchDisplayedAttributes` which allows to configure the attributes that are displayed in the user search results.
|
||||
We are also deprecating the `ShowUserEmailInResults` configuration option.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/11695
|
||||
@@ -26,17 +26,18 @@ type Config struct {
|
||||
|
||||
SkipUserGroupsInToken bool `yaml:"skip_user_groups_in_token" env:"FRONTEND_SKIP_USER_GROUPS_IN_TOKEN" desc:"Disables the loading of user's group memberships from the reva access token." introductionVersion:"pre5.0"`
|
||||
|
||||
EnableFavorites bool `yaml:"enable_favorites" env:"FRONTEND_ENABLE_FAVORITES" desc:"Enables the support for favorites in the clients." introductionVersion:"pre5.0"`
|
||||
MaxQuota uint64 `yaml:"max_quota" env:"OCIS_SPACES_MAX_QUOTA;FRONTEND_MAX_QUOTA" desc:"Set the global max quota value in bytes. A value of 0 equals unlimited. The value is provided via capabilities." introductionVersion:"pre5.0"`
|
||||
UploadMaxChunkSize int `yaml:"upload_max_chunk_size" env:"FRONTEND_UPLOAD_MAX_CHUNK_SIZE" desc:"Sets the max chunk sizes in bytes for uploads via the clients." introductionVersion:"pre5.0"`
|
||||
UploadHTTPMethodOverride string `yaml:"upload_http_method_override" env:"FRONTEND_UPLOAD_HTTP_METHOD_OVERRIDE" desc:"Advise TUS to replace PATCH requests by POST requests." introductionVersion:"pre5.0"`
|
||||
DefaultUploadProtocol string `yaml:"default_upload_protocol" env:"FRONTEND_DEFAULT_UPLOAD_PROTOCOL" desc:"The default upload protocol to use in clients. Currently only 'tus' is available. See the developer API documentation for more details about TUS." introductionVersion:"pre5.0"`
|
||||
EnableFederatedSharingIncoming bool `yaml:"enable_federated_sharing_incoming" env:"OCIS_ENABLE_OCM;FRONTEND_ENABLE_FEDERATED_SHARING_INCOMING" desc:"Changing this value is NOT supported. Enables support for incoming federated sharing for clients. The backend behaviour is not changed." introductionVersion:"pre5.0"`
|
||||
EnableFederatedSharingOutgoing bool `yaml:"enable_federated_sharing_outgoing" env:"OCIS_ENABLE_OCM;FRONTEND_ENABLE_FEDERATED_SHARING_OUTGOING" desc:"Changing this value is NOT supported. Enables support for outgoing federated sharing for clients. The backend behaviour is not changed." introductionVersion:"pre5.0"`
|
||||
SearchMinLength int `yaml:"search_min_length" env:"FRONTEND_SEARCH_MIN_LENGTH" desc:"Minimum number of characters to enter before a client should start a search for Share receivers. This setting can be used to customize the user experience if e.g too many results are displayed." introductionVersion:"pre5.0"`
|
||||
Edition string `yaml:"edition" env:"OCIS_EDITION;FRONTEND_EDITION" desc:"Edition of oCIS. Used for branding purposes." introductionVersion:"pre5.0"`
|
||||
DisableSSE bool `yaml:"disable_sse" env:"OCIS_DISABLE_SSE;FRONTEND_DISABLE_SSE" desc:"When set to true, clients are informed that the Server-Sent Events endpoint is not accessible." introductionVersion:"pre5.0"`
|
||||
DefaultLinkPermissions int `yaml:"default_link_permissions" env:"FRONTEND_DEFAULT_LINK_PERMISSIONS" desc:"Defines the default permissions a link is being created with. Possible values are 0 (= internal link, for instance members only) and 1 (= public link with viewer permissions). Defaults to 1." introductionVersion:"5.0"`
|
||||
EnableFavorites bool `yaml:"enable_favorites" env:"FRONTEND_ENABLE_FAVORITES" desc:"Enables the support for favorites in the clients." introductionVersion:"pre5.0"`
|
||||
MaxQuota uint64 `yaml:"max_quota" env:"OCIS_SPACES_MAX_QUOTA;FRONTEND_MAX_QUOTA" desc:"Set the global max quota value in bytes. A value of 0 equals unlimited. The value is provided via capabilities." introductionVersion:"pre5.0"`
|
||||
UploadMaxChunkSize int `yaml:"upload_max_chunk_size" env:"FRONTEND_UPLOAD_MAX_CHUNK_SIZE" desc:"Sets the max chunk sizes in bytes for uploads via the clients." introductionVersion:"pre5.0"`
|
||||
UploadHTTPMethodOverride string `yaml:"upload_http_method_override" env:"FRONTEND_UPLOAD_HTTP_METHOD_OVERRIDE" desc:"Advise TUS to replace PATCH requests by POST requests." introductionVersion:"pre5.0"`
|
||||
DefaultUploadProtocol string `yaml:"default_upload_protocol" env:"FRONTEND_DEFAULT_UPLOAD_PROTOCOL" desc:"The default upload protocol to use in clients. Currently only 'tus' is available. See the developer API documentation for more details about TUS." introductionVersion:"pre5.0"`
|
||||
EnableFederatedSharingIncoming bool `yaml:"enable_federated_sharing_incoming" env:"OCIS_ENABLE_OCM;FRONTEND_ENABLE_FEDERATED_SHARING_INCOMING" desc:"Changing this value is NOT supported. Enables support for incoming federated sharing for clients. The backend behaviour is not changed." introductionVersion:"pre5.0"`
|
||||
EnableFederatedSharingOutgoing bool `yaml:"enable_federated_sharing_outgoing" env:"OCIS_ENABLE_OCM;FRONTEND_ENABLE_FEDERATED_SHARING_OUTGOING" desc:"Changing this value is NOT supported. Enables support for outgoing federated sharing for clients. The backend behaviour is not changed." introductionVersion:"pre5.0"`
|
||||
SearchMinLength int `yaml:"search_min_length" env:"FRONTEND_SEARCH_MIN_LENGTH" desc:"Minimum number of characters to enter before a client should start a search for Share receivers. This setting can be used to customize the user experience if e.g too many results are displayed." introductionVersion:"pre5.0"`
|
||||
UserSearchDisplayedAttributes []string `yaml:"user_search_displayed_attributes" env:"OCIS_USER_SEARCH_DISPLAYED_ATTRIBUTES;FRONTEND_USER_SEARCH_DISPLAYED_ATTRIBUTES" desc:"A list of user attributes to display in the user search results." introductionVersion:"Balch"`
|
||||
Edition string `yaml:"edition" env:"OCIS_EDITION;FRONTEND_EDITION" desc:"Edition of oCIS. Used for branding purposes." introductionVersion:"pre5.0"`
|
||||
DisableSSE bool `yaml:"disable_sse" env:"OCIS_DISABLE_SSE;FRONTEND_DISABLE_SSE" desc:"When set to true, clients are informed that the Server-Sent Events endpoint is not accessible." introductionVersion:"pre5.0"`
|
||||
DefaultLinkPermissions int `yaml:"default_link_permissions" env:"FRONTEND_DEFAULT_LINK_PERMISSIONS" desc:"Defines the default permissions a link is being created with. Possible values are 0 (= internal link, for instance members only) and 1 (= public link with viewer permissions). Defaults to 1." introductionVersion:"5.0"`
|
||||
|
||||
PublicURL string `yaml:"public_url" env:"OCIS_URL;FRONTEND_PUBLIC_URL" desc:"The public facing URL of the oCIS frontend." introductionVersion:"pre5.0"`
|
||||
MaxConcurrency int `yaml:"max_concurrency" env:"OCIS_MAX_CONCURRENCY;FRONTEND_MAX_CONCURRENCY" desc:"Maximum number of concurrent go-routines. Higher values can potentially get work done faster but will also cause more load on the system. Values of 0 or below will be ignored and the default value will be used." introductionVersion:"7.0.0"`
|
||||
@@ -150,7 +151,7 @@ type OCS struct {
|
||||
IncludeOCMSharees bool `yaml:"include_ocm_sharees" env:"OCIS_ENABLE_OCM" desc:"Include OCM sharees when listing sharees." introductionVersion:"5.0" deprecationVersion:"7.0.0" removalVersion:"%%NEXT_PRODUCTION_VERSION%%" deprecationInfo:"The OCS API is deprecated" deprecationReplacement:""`
|
||||
PublicShareMustHavePassword bool `yaml:"public_sharing_share_must_have_password" env:"OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD" desc:"Set this to true if you want to enforce passwords on all public shares." introductionVersion:"5.0" deprecationVersion:"7.0.0" removalVersion:"%%NEXT_PRODUCTION_VERSION%%" deprecationInfo:"The OCS API is deprecated" deprecationReplacement:""`
|
||||
WriteablePublicShareMustHavePassword bool `yaml:"public_sharing_writeableshare_must_have_password" env:"OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD" desc:"Set this to true if you want to enforce passwords for writable shares. Only effective if the setting for 'passwords on all public shares' is set to false." introductionVersion:"5.0" deprecationVersion:"7.0.0" removalVersion:"%%NEXT_PRODUCTION_VERSION%%" deprecationInfo:"The OCS API is deprecated" deprecationReplacement:""`
|
||||
ShowUserEmailInResults bool `yaml:"show_email_in_results" env:"OCIS_SHOW_USER_EMAIL_IN_RESULTS" desc:"Include user email addresses in responses. If absent or set to false emails will be omitted from results. Please note that admin users can always see all email addresses." introductionVersion:"6.0.0"`
|
||||
ShowUserEmailInResults bool `yaml:"show_email_in_results" env:"OCIS_SHOW_USER_EMAIL_IN_RESULTS" desc:"Include user email addresses in responses. If absent or set to false emails will be omitted from results. Please note that admin users can always see all email addresses." introductionVersion:"6.0.0" deprecationVersion:"Balch" removalVersion:"%%NEXT_PRODUCTION_VERSION%%" deprecationInfo:"Deprecating in favor of a unified array with arbitrary attributes" deprecationReplacement:"UserSearchDisplayedAttributes"`
|
||||
}
|
||||
|
||||
type CacheWarmupDrivers struct {
|
||||
|
||||
@@ -357,8 +357,9 @@ func FrontendConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string
|
||||
"productversion": version.GetString(),
|
||||
},
|
||||
},
|
||||
"include_ocm_sharees": cfg.OCS.IncludeOCMSharees,
|
||||
"show_email_in_results": cfg.OCS.ShowUserEmailInResults,
|
||||
"include_ocm_sharees": cfg.OCS.IncludeOCMSharees,
|
||||
"show_email_in_results": cfg.OCS.ShowUserEmailInResults,
|
||||
"user_search_displayed_attributes": cfg.UserSearchDisplayedAttributes,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -114,11 +114,12 @@ type Identity struct {
|
||||
|
||||
// API represents API configuration parameters.
|
||||
type API struct {
|
||||
GroupMembersPatchLimit int `yaml:"group_members_patch_limit" env:"GRAPH_GROUP_MEMBERS_PATCH_LIMIT" desc:"The amount of group members allowed to be added with a single patch request." introductionVersion:"pre5.0"`
|
||||
UsernameMatch string `yaml:"graph_username_match" env:"GRAPH_USERNAME_MATCH" desc:"Apply restrictions to usernames. Supported values are 'default' and 'none'. When set to 'default', user names must not start with a number and are restricted to ASCII characters. When set to 'none', no restrictions are applied. The default value is 'default'." introductionVersion:"pre5.0"`
|
||||
AssignDefaultUserRole bool `yaml:"graph_assign_default_user_role" env:"GRAPH_ASSIGN_DEFAULT_USER_ROLE" desc:"Whether to assign newly created users the default role 'User'. Set this to 'false' if you want to assign roles manually, or if the role assignment should happen at first login. Set this to 'true' (the default) to assign the role 'User' when creating a new user." introductionVersion:"pre5.0"`
|
||||
IdentitySearchMinLength int `yaml:"graph_identity_search_min_length" env:"GRAPH_IDENTITY_SEARCH_MIN_LENGTH" desc:"The minimum length the search term needs to have for unprivileged users when searching for users or groups." introductionVersion:"5.0"`
|
||||
ShowUserEmailInResults bool `yaml:"show_email_in_results" env:"OCIS_SHOW_USER_EMAIL_IN_RESULTS" desc:"Include user email addresses in responses. If absent or set to false emails will be omitted from results. Please note that admin users can always see all email addresses." introductionVersion:"6.0.0"`
|
||||
GroupMembersPatchLimit int `yaml:"group_members_patch_limit" env:"GRAPH_GROUP_MEMBERS_PATCH_LIMIT" desc:"The amount of group members allowed to be added with a single patch request." introductionVersion:"pre5.0"`
|
||||
UsernameMatch string `yaml:"graph_username_match" env:"GRAPH_USERNAME_MATCH" desc:"Apply restrictions to usernames. Supported values are 'default' and 'none'. When set to 'default', user names must not start with a number and are restricted to ASCII characters. When set to 'none', no restrictions are applied. The default value is 'default'." introductionVersion:"pre5.0"`
|
||||
AssignDefaultUserRole bool `yaml:"graph_assign_default_user_role" env:"GRAPH_ASSIGN_DEFAULT_USER_ROLE" desc:"Whether to assign newly created users the default role 'User'. Set this to 'false' if you want to assign roles manually, or if the role assignment should happen at first login. Set this to 'true' (the default) to assign the role 'User' when creating a new user." introductionVersion:"pre5.0"`
|
||||
IdentitySearchMinLength int `yaml:"graph_identity_search_min_length" env:"GRAPH_IDENTITY_SEARCH_MIN_LENGTH" desc:"The minimum length the search term needs to have for unprivileged users when searching for users or groups." introductionVersion:"5.0"`
|
||||
ShowUserEmailInResults bool `yaml:"show_email_in_results" env:"OCIS_SHOW_USER_EMAIL_IN_RESULTS" desc:"Include user email addresses in responses. If absent or set to false emails will be omitted from results. Please note that admin users can always see all email addresses." introductionVersion:"6.0.0" deprecationVersion:"Balch" removalVersion:"%%NEXT_PRODUCTION_VERSION%%" deprecationInfo:"Deprecating in favor of a unified array with arbitrary attributes" deprecationReplacement:"UserSearchDisplayedAttributes"`
|
||||
UserSearchDisplayedAttributes []string `yaml:"user_search_displayed_attributes" env:"OCIS_USER_SEARCH_DISPLAYED_ATTRIBUTES" desc:"The attributes to display in the user search results." introductionVersion:"Balch"`
|
||||
}
|
||||
|
||||
// Events combines the configuration options for the event bus.
|
||||
|
||||
@@ -2,6 +2,7 @@ package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -209,6 +210,92 @@ func (g Graph) contextUserHasFullAccountPerms(reqctx context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// UserWithAttributes is a wrapper for the User type that includes attributes.
|
||||
// Attributes are a list of attributes that are displayed in the user search results.
|
||||
type UserWithAttributes struct {
|
||||
*libregraph.User
|
||||
Attributes []string `json:"attributes"`
|
||||
}
|
||||
|
||||
// MarshalJSON is a custom marshaler for the UserWithAttributes type.
|
||||
// It marshals the User type and the Attributes type.
|
||||
func (u UserWithAttributes) MarshalJSON() ([]byte, error) {
|
||||
if u.User == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
|
||||
userMap, err := u.User.ToMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.Attributes != nil {
|
||||
userMap["attributes"] = u.Attributes
|
||||
} else {
|
||||
userMap["attributes"] = []string{}
|
||||
}
|
||||
|
||||
return json.Marshal(userMap)
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a custom unmarshaler for the UserWithAttributes type.
|
||||
// It unmarshals the User type and the Attributes type.
|
||||
func (u *UserWithAttributes) UnmarshalJSON(data []byte) error {
|
||||
raw := make(map[string]json.RawMessage)
|
||||
if err := json.Unmarshal(data, &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if attrData, exists := raw["attributes"]; exists {
|
||||
if err := json.Unmarshal(attrData, &u.Attributes); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(raw, "attributes")
|
||||
}
|
||||
|
||||
if userJSON, err := json.Marshal(raw); err != nil {
|
||||
return err
|
||||
} else if err := json.Unmarshal(userJSON, &u.User); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getUsersAttributes returns the attributes of the user that are in the allowed list.
|
||||
func getUsersAttributes(displayedAttributes []string, user *libregraph.User) ([]string, error) {
|
||||
userMap, err := user.ToMap()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
attributes := []string{}
|
||||
|
||||
for attrStr, val := range userMap {
|
||||
if !slices.Contains(displayedAttributes, attrStr) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
attributes = append(attributes, v)
|
||||
case *string:
|
||||
if v != nil {
|
||||
attributes = append(attributes, *v)
|
||||
}
|
||||
case []libregraph.Group:
|
||||
groups := userMap[attrStr].([]libregraph.Group)
|
||||
for _, group := range groups {
|
||||
attributes = append(attributes, *group.DisplayName)
|
||||
}
|
||||
default:
|
||||
// skip unsupported types
|
||||
}
|
||||
}
|
||||
|
||||
return attributes, nil
|
||||
}
|
||||
|
||||
// GetUsers implements the Service interface.
|
||||
func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
@@ -292,24 +379,6 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// If the user isn't admin, we'll show just the minimum user attibutes
|
||||
if !ctxHasFullPerms {
|
||||
finalUsers := make([]*libregraph.User, len(users))
|
||||
for i, u := range users {
|
||||
finalUsers[i] = &libregraph.User{
|
||||
Id: u.Id,
|
||||
DisplayName: u.DisplayName,
|
||||
UserType: u.UserType,
|
||||
Identities: u.Identities,
|
||||
}
|
||||
|
||||
if g.config.API.ShowUserEmailInResults {
|
||||
finalUsers[i].Mail = u.Mail
|
||||
}
|
||||
}
|
||||
users = finalUsers
|
||||
}
|
||||
|
||||
exp, err := identity.GetExpandValues(odataReq.Query)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users: $expand error")
|
||||
@@ -340,8 +409,42 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
usersWithAttributes := make([]*UserWithAttributes, 0, len(users))
|
||||
displayedAttributes := g.config.API.UserSearchDisplayedAttributes
|
||||
if g.config.API.ShowUserEmailInResults && !slices.Contains(displayedAttributes, "mail") {
|
||||
displayedAttributes = append([]string{"mail"}, displayedAttributes...)
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
attributes, err := getUsersAttributes(displayedAttributes, user)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("user", user.GetId()).Msg("could not get user attributes")
|
||||
}
|
||||
|
||||
// If the user isn't admin, we'll show just the minimum user attributes
|
||||
finalUser := &libregraph.User{
|
||||
Id: user.Id,
|
||||
DisplayName: user.DisplayName,
|
||||
UserType: user.UserType,
|
||||
Identities: user.Identities,
|
||||
}
|
||||
|
||||
if ctxHasFullPerms {
|
||||
finalUser = user
|
||||
} else if g.config.API.ShowUserEmailInResults || slices.Contains(displayedAttributes, "mail") {
|
||||
// Remove this once `ShowUserEmailInResults` is removed
|
||||
finalUser.Mail = user.Mail
|
||||
}
|
||||
|
||||
usersWithAttributes = append(usersWithAttributes, &UserWithAttributes{
|
||||
User: finalUser,
|
||||
Attributes: attributes,
|
||||
})
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, &ListResponse{Value: users})
|
||||
render.JSON(w, r, &ListResponse{Value: usersWithAttributes})
|
||||
}
|
||||
|
||||
// PostUser implements the Service interface.
|
||||
@@ -567,7 +670,7 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
if !g.config.API.ShowUserEmailInResults {
|
||||
if !g.config.API.ShowUserEmailInResults && !slices.Contains(g.config.API.UserSearchDisplayedAttributes, "mail") {
|
||||
user.Mail = nil
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ import (
|
||||
)
|
||||
|
||||
type userList struct {
|
||||
Value []*libregraph.User
|
||||
Value []*service.UserWithAttributes
|
||||
}
|
||||
|
||||
var _ = Describe("Users", func() {
|
||||
@@ -344,6 +344,91 @@ var _ = Describe("Users", func() {
|
||||
}
|
||||
})
|
||||
|
||||
Describe("user attributes", func() {
|
||||
var (
|
||||
user *libregraph.User
|
||||
users []*libregraph.User
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
cfg.API.UserSearchDisplayedAttributes = []string{"displayName", "onPremisesSamAccountName"}
|
||||
cfg.API.ShowUserEmailInResults = true
|
||||
|
||||
user = libregraph.NewUser("Albert Einstein", "einstein")
|
||||
user.SetId("user1")
|
||||
user.SetMail("albert.einstein@example.com")
|
||||
user.SetUserType("Member")
|
||||
user.SetPasswordProfile(libregraph.PasswordProfile{Password: libregraph.PtrString("secret")})
|
||||
users = []*libregraph.User{user}
|
||||
|
||||
identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
|
||||
})
|
||||
|
||||
It("returns full user objects and configured attributes for privileged users", func() {
|
||||
permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{
|
||||
Permission: &settingsmsg.Permission{
|
||||
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
|
||||
},
|
||||
}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$search=einstein", nil)
|
||||
r = r.WithContext(mfa.Set(r.Context(), true))
|
||||
svc.GetUsers(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
var res userList
|
||||
err = json.Unmarshal(data, &res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(res.Value).To(HaveLen(1))
|
||||
|
||||
Expect(res.Value[0].User.GetId()).To(Equal("user1"))
|
||||
Expect(res.Value[0].User.GetDisplayName()).To(Equal("Albert Einstein"))
|
||||
Expect(res.Value[0].User.GetOnPremisesSamAccountName()).To(Equal("einstein"))
|
||||
Expect(res.Value[0].User.GetMail()).To(Equal("albert.einstein@example.com"))
|
||||
Expect(res.Value[0].User.HasPasswordProfile()).To(BeTrue())
|
||||
|
||||
Expect(res.Value[0].Attributes).To(ConsistOf(
|
||||
"Albert Einstein",
|
||||
"einstein",
|
||||
"albert.einstein@example.com",
|
||||
))
|
||||
})
|
||||
|
||||
It("returns restricted user objects and configured attributes for unprivileged users", func() {
|
||||
permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settings.GetPermissionByIDResponse{}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$search=einstein", nil)
|
||||
svc.GetUsers(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
var res userList
|
||||
err = json.Unmarshal(data, &res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(res.Value).To(HaveLen(1))
|
||||
|
||||
Expect(res.Value[0].User.GetId()).To(Equal("user1"))
|
||||
Expect(res.Value[0].User.GetDisplayName()).To(Equal("Albert Einstein"))
|
||||
Expect(res.Value[0].User.GetUserType()).To(Equal("Member"))
|
||||
Expect(res.Value[0].User.GetOnPremisesSamAccountName()).To(Equal(""))
|
||||
Expect(res.Value[0].User.HasMail()).To(BeTrue())
|
||||
Expect(res.Value[0].User.HasPasswordProfile()).To(BeFalse())
|
||||
|
||||
Expect(res.Value[0].Attributes).To(ConsistOf(
|
||||
"Albert Einstein",
|
||||
"einstein",
|
||||
"albert.einstein@example.com",
|
||||
))
|
||||
})
|
||||
})
|
||||
|
||||
It("sorts", func() {
|
||||
user := &libregraph.User{}
|
||||
user.SetId("user1")
|
||||
@@ -366,7 +451,7 @@ var _ = Describe("Users", func() {
|
||||
},
|
||||
}, nil)
|
||||
|
||||
getUsers := func(path string) []*libregraph.User {
|
||||
getUsers := func(path string) []*service.UserWithAttributes {
|
||||
r := httptest.NewRequest(http.MethodGet, path, nil)
|
||||
r = r.WithContext(mfa.Set(r.Context(), true))
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
@@ -1050,6 +1050,7 @@ Feature: get users
|
||||
"type": "object",
|
||||
"required": ["displayName", "id", "userType", "onPremisesSamAccountName"],
|
||||
"properties": {
|
||||
"attributes": {"const": []},
|
||||
"displayName": {"const": "special user"},
|
||||
"id": {"pattern": "^%user_id_pattern%$"},
|
||||
"userType": {"const": "Member"},
|
||||
|
||||
@@ -272,12 +272,17 @@ Feature: edit/search user including email
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"attributes",
|
||||
"displayName",
|
||||
"id",
|
||||
"mail",
|
||||
"userType"
|
||||
],
|
||||
"properties": {
|
||||
"attributes": {
|
||||
"type": "array",
|
||||
"maxItems": 1,
|
||||
"minItems": 1
|
||||
},
|
||||
"displayName": {
|
||||
"const": "Alice Hansen"
|
||||
},
|
||||
@@ -285,9 +290,6 @@ Feature: edit/search user including email
|
||||
"type": "string",
|
||||
"pattern": "^%user_id_pattern%$"
|
||||
},
|
||||
"mail": {
|
||||
"const": "alice@example.org"
|
||||
},
|
||||
"userType": {
|
||||
"const": "Member"
|
||||
}
|
||||
@@ -320,12 +322,17 @@ Feature: edit/search user including email
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"attributes",
|
||||
"displayName",
|
||||
"id",
|
||||
"mail",
|
||||
"userType"
|
||||
],
|
||||
"properties": {
|
||||
"attributes": {
|
||||
"type": "array",
|
||||
"maxItems": 1,
|
||||
"minItems": 1
|
||||
},
|
||||
"displayName": {
|
||||
"const": "Alice Hansen"
|
||||
},
|
||||
@@ -364,12 +371,17 @@ Feature: edit/search user including email
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"attributes",
|
||||
"displayName",
|
||||
"id",
|
||||
"mail",
|
||||
"userType"
|
||||
],
|
||||
"properties": {
|
||||
"attributes": {
|
||||
"type": "array",
|
||||
"maxItems": 1,
|
||||
"minItems": 1
|
||||
},
|
||||
"displayName": {
|
||||
"const": "Alice Hansen"
|
||||
},
|
||||
@@ -415,12 +427,17 @@ Feature: edit/search user including email
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"attributes",
|
||||
"displayName",
|
||||
"id",
|
||||
"mail",
|
||||
"userType"
|
||||
],
|
||||
"properties": {
|
||||
"attributes": {
|
||||
"type": "array",
|
||||
"maxItems": 1,
|
||||
"minItems": 1
|
||||
},
|
||||
"displayName": {
|
||||
"const": "Alice Hansen"
|
||||
},
|
||||
@@ -439,12 +456,17 @@ Feature: edit/search user including email
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"attributes",
|
||||
"displayName",
|
||||
"id",
|
||||
"mail",
|
||||
"userType"
|
||||
],
|
||||
"properties": {
|
||||
"attributes": {
|
||||
"type": "array",
|
||||
"maxItems": 1,
|
||||
"minItems": 1
|
||||
},
|
||||
"displayName": {
|
||||
"const": "Alice Murphy"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user