mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-06 04:09:40 -06:00
feat(proxy): Update selected attributes of autoprovisioned users
When autoprovisioning is enabled, we now update autoprovisioned users when their display name or email address claims change. Closes: #8955
This commit is contained in:
committed by
Ralf Haferkamp
parent
cd7b15b250
commit
7ca8391ce2
@@ -134,6 +134,14 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if m.autoProvisionAccounts {
|
||||
if err = m.userProvider.UpdateUserIfNeeded(req.Context(), user, claims); err != nil {
|
||||
m.logger.Error().Err(err).Str("userid", user.GetId().GetOpaqueId()).Interface("claims", claims).Msg("Failed to update autoprovisioned user")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// resolve the user's roles
|
||||
user, err = m.userRoleAssigner.UpdateUserRoleAssignment(ctx, user, claims)
|
||||
if err != nil {
|
||||
|
||||
@@ -21,4 +21,5 @@ type UserBackend interface {
|
||||
GetUserByClaims(ctx context.Context, claim, value string) (*cs3.User, string, error)
|
||||
Authenticate(ctx context.Context, username string, password string) (*cs3.User, string, error)
|
||||
CreateUserFromClaims(ctx context.Context, claims map[string]interface{}) (*cs3.User, error)
|
||||
UpdateUserIfNeeded(ctx context.Context, user *cs3.User, claims map[string]interface{}) error
|
||||
}
|
||||
|
||||
@@ -224,6 +224,62 @@ func (c *cs3backend) CreateUserFromClaims(ctx context.Context, claims map[string
|
||||
return &cs3UserCreated, nil
|
||||
}
|
||||
|
||||
func (c cs3backend) UpdateUserIfNeeded(ctx context.Context, user *cs3.User, claims map[string]interface{}) error {
|
||||
newUser, err := c.libregraphUserFromClaims(claims)
|
||||
if err != nil {
|
||||
c.logger.Error().Err(err).Interface("claims", claims).Msg("Error converting claims to user")
|
||||
return fmt.Errorf("error converting claims to updated user: %w", err)
|
||||
}
|
||||
|
||||
// Check if the user needs to be updated, only updates of "displayName" and "mail" are supported
|
||||
// currently.
|
||||
switch {
|
||||
case newUser.GetDisplayName() != user.GetDisplayName():
|
||||
fallthrough
|
||||
case newUser.GetMail() != user.GetMail():
|
||||
return c.updateLibregraphUser(user.GetId().GetOpaqueId(), newUser)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c cs3backend) updateLibregraphUser(userid string, user libregraph.User) error {
|
||||
gatewayClient, err := c.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
c.logger.Error().Err(err).Msg("could not select next gateway client")
|
||||
return err
|
||||
}
|
||||
newctx := context.Background()
|
||||
authRes, err := gatewayClient.Authenticate(newctx, &gateway.AuthenticateRequest{
|
||||
Type: "serviceaccounts",
|
||||
ClientId: c.serviceAccount.ServiceAccountID,
|
||||
ClientSecret: c.serviceAccount.ServiceAccountSecret,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK {
|
||||
return fmt.Errorf("error authenticating service user: %s", authRes.GetStatus().GetMessage())
|
||||
}
|
||||
|
||||
lgClient, err := c.setupLibregraphClient(newctx, authRes.GetToken())
|
||||
if err != nil {
|
||||
c.logger.Error().Err(err).Msg("Error setting up libregraph client")
|
||||
return err
|
||||
}
|
||||
|
||||
req := lgClient.UserApi.UpdateUser(newctx, userid).User(user)
|
||||
|
||||
_, resp, err := req.Execute()
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
c.logger.Error().Err(err).Msg("Failed to update user via libregraph")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c cs3backend) setupLibregraphClient(ctx context.Context, cs3token string) (*libregraph.APIClient, error) {
|
||||
// Use micro registry to resolve next graph service endpoint
|
||||
next, err := c.graphSelector.Select("com.owncloud.graph.graph")
|
||||
|
||||
@@ -215,6 +215,54 @@ func (_c *UserBackend_GetUserByClaims_Call) RunAndReturn(run func(context.Contex
|
||||
return _c
|
||||
}
|
||||
|
||||
// UpdateUserIfNeeded provides a mock function with given fields: ctx, user, claims
|
||||
func (_m *UserBackend) UpdateUserIfNeeded(ctx context.Context, user *userv1beta1.User, claims map[string]interface{}) error {
|
||||
ret := _m.Called(ctx, user, claims)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for UpdateUserIfNeeded")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *userv1beta1.User, map[string]interface{}) error); ok {
|
||||
r0 = rf(ctx, user, claims)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// UserBackend_UpdateUserIfNeeded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUserIfNeeded'
|
||||
type UserBackend_UpdateUserIfNeeded_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// UpdateUserIfNeeded is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - user *userv1beta1.User
|
||||
// - claims map[string]interface{}
|
||||
func (_e *UserBackend_Expecter) UpdateUserIfNeeded(ctx interface{}, user interface{}, claims interface{}) *UserBackend_UpdateUserIfNeeded_Call {
|
||||
return &UserBackend_UpdateUserIfNeeded_Call{Call: _e.mock.On("UpdateUserIfNeeded", ctx, user, claims)}
|
||||
}
|
||||
|
||||
func (_c *UserBackend_UpdateUserIfNeeded_Call) Run(run func(ctx context.Context, user *userv1beta1.User, claims map[string]interface{})) *UserBackend_UpdateUserIfNeeded_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(*userv1beta1.User), args[2].(map[string]interface{}))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *UserBackend_UpdateUserIfNeeded_Call) Return(_a0 error) *UserBackend_UpdateUserIfNeeded_Call {
|
||||
_c.Call.Return(_a0)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *UserBackend_UpdateUserIfNeeded_Call) RunAndReturn(run func(context.Context, *userv1beta1.User, map[string]interface{}) error) *UserBackend_UpdateUserIfNeeded_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewUserBackend creates a new instance of UserBackend. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewUserBackend(t interface {
|
||||
|
||||
Reference in New Issue
Block a user