Auth: Set the default org after User login (#83918)

* poc

* add logger, skip hook when user is not assigned to default org

* Add tests, move to hook folder

* docs

* Skip for OrgId < 1

* Address feedback

* Update docs/sources/setup-grafana/configure-grafana/_index.md

* lint

* Move the hook to org_sync.go

* Update pkg/services/authn/authnimpl/sync/org_sync.go

* Handle the case when GetUserOrgList returns error

---------

Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
Co-authored-by: Karl Persson <kalle.persson@grafana.com>
This commit is contained in:
Misi 2024-03-12 09:35:13 +01:00 committed by GitHub
parent 8c06c0dea7
commit 63f1c30313
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 687 additions and 6 deletions

View File

@ -473,6 +473,9 @@ auto_assign_org_role = Viewer
# Require email validation before sign up completes # Require email validation before sign up completes
verify_email_enabled = false verify_email_enabled = false
# Redirect to default OrgId after login
login_default_org_id =
# Background text for the user field on the login page # Background text for the user field on the login page
login_hint = email or username login_hint = email or username
password_hint = password password_hint = password

View File

@ -449,6 +449,9 @@
# Require email validation before sign up completes # Require email validation before sign up completes
;verify_email_enabled = false ;verify_email_enabled = false
# Redirect to default OrgId after login
;login_default_org_id =
# Background text for the user field on the login page # Background text for the user field on the login page
;login_hint = email or username ;login_hint = email or username
;password_hint = password ;password_hint = password

View File

@ -820,6 +820,10 @@ The available options are `Viewer` (default), `Admin`, `Editor`, and `None`. For
Require email validation before sign up completes or when updating a user email address. Default is `false`. Require email validation before sign up completes or when updating a user email address. Default is `false`.
### login_default_org_id
Set the default organization for users when they sign in. The default is `-1`.
### login_hint ### login_hint
Text used as placeholder text on login page for login/username input. Text used as placeholder text on login page for login/username input.

View File

@ -147,7 +147,7 @@ func ProvideService(
// FIXME (jguer): move to User package // FIXME (jguer): move to User package
userSyncService := sync.ProvideUserSync(userService, userProtectionService, authInfoService, quotaService) userSyncService := sync.ProvideUserSync(userService, userProtectionService, authInfoService, quotaService)
orgUserSyncService := sync.ProvideOrgSync(userService, orgService, accessControlService) orgUserSyncService := sync.ProvideOrgSync(userService, orgService, accessControlService, cfg)
s.RegisterPostAuthHook(userSyncService.SyncUserHook, 10) s.RegisterPostAuthHook(userSyncService.SyncUserHook, 10)
s.RegisterPostAuthHook(userSyncService.EnableUserHook, 20) s.RegisterPostAuthHook(userSyncService.EnableUserHook, 20)
s.RegisterPostAuthHook(orgUserSyncService.SyncOrgRolesHook, 30) s.RegisterPostAuthHook(orgUserSyncService.SyncOrgRolesHook, 30)
@ -162,6 +162,8 @@ func ProvideService(
s.RegisterPostAuthHook(rbacSync.SyncPermissionsHook, 120) s.RegisterPostAuthHook(rbacSync.SyncPermissionsHook, 120)
s.RegisterPostAuthHook(orgUserSyncService.SetDefaultOrgHook, 130)
return s return s
} }

View File

@ -3,6 +3,7 @@ package sync
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"sort" "sort"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
@ -11,16 +12,18 @@ import (
"github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
) )
func ProvideOrgSync(userService user.Service, orgService org.Service, accessControl accesscontrol.Service) *OrgSync { func ProvideOrgSync(userService user.Service, orgService org.Service, accessControl accesscontrol.Service, cfg *setting.Cfg) *OrgSync {
return &OrgSync{userService, orgService, accessControl, log.New("org.sync")} return &OrgSync{userService, orgService, accessControl, cfg, log.New("org.sync")}
} }
type OrgSync struct { type OrgSync struct {
userService user.Service userService user.Service
orgService org.Service orgService org.Service
accessControl accesscontrol.Service accessControl accesscontrol.Service
cfg *setting.Cfg
log log.Logger log log.Logger
} }
@ -72,7 +75,7 @@ func (s *OrgSync) SyncOrgRolesHook(ctx context.Context, id *authn.Identity, _ *a
// update role // update role
cmd := &org.UpdateOrgUserCommand{OrgID: orga.OrgID, UserID: userID, Role: extRole} cmd := &org.UpdateOrgUserCommand{OrgID: orga.OrgID, UserID: userID, Role: extRole}
if err := s.orgService.UpdateOrgUser(ctx, cmd); err != nil { if err := s.orgService.UpdateOrgUser(ctx, cmd); err != nil {
s.log.FromContext(ctx).Error("Failed to update active org user", "id", id.ID, "error", err) ctxLogger.Error("Failed to update active org user", "id", id.ID, "error", err)
return err return err
} }
} }
@ -90,7 +93,7 @@ func (s *OrgSync) SyncOrgRolesHook(ctx context.Context, id *authn.Identity, _ *a
cmd := &org.AddOrgUserCommand{UserID: userID, Role: orgRole, OrgID: orgId} cmd := &org.AddOrgUserCommand{UserID: userID, Role: orgRole, OrgID: orgId}
err := s.orgService.AddOrgUser(ctx, cmd) err := s.orgService.AddOrgUser(ctx, cmd)
if err != nil && !errors.Is(err, org.ErrOrgNotFound) { if err != nil && !errors.Is(err, org.ErrOrgNotFound) {
s.log.FromContext(ctx).Error("Failed to update active org for user", "id", id.ID, "error", err) ctxLogger.Error("Failed to update active org for user", "id", id.ID, "error", err)
return err return err
} }
} }
@ -100,7 +103,7 @@ func (s *OrgSync) SyncOrgRolesHook(ctx context.Context, id *authn.Identity, _ *a
ctxLogger.Debug("Removing user's organization membership as part of syncing with OAuth login", "id", id.ID, "orgId", orgID) ctxLogger.Debug("Removing user's organization membership as part of syncing with OAuth login", "id", id.ID, "orgId", orgID)
cmd := &org.RemoveOrgUserCommand{OrgID: orgID, UserID: userID} cmd := &org.RemoveOrgUserCommand{OrgID: orgID, UserID: userID}
if err := s.orgService.RemoveOrgUser(ctx, cmd); err != nil { if err := s.orgService.RemoveOrgUser(ctx, cmd); err != nil {
s.log.FromContext(ctx).Error("Failed to remove user from org", "id", id.ID, "orgId", orgID, "error", err) ctxLogger.Error("Failed to remove user from org", "id", id.ID, "orgId", orgID, "error", err)
if errors.Is(err, org.ErrLastOrgAdmin) { if errors.Is(err, org.ErrLastOrgAdmin) {
continue continue
} }
@ -128,3 +131,59 @@ func (s *OrgSync) SyncOrgRolesHook(ctx context.Context, id *authn.Identity, _ *a
return nil return nil
} }
func (s *OrgSync) SetDefaultOrgHook(ctx context.Context, currentIdentity *authn.Identity, r *authn.Request) error {
if s.cfg.LoginDefaultOrgId < 1 || currentIdentity == nil {
return nil
}
ctxLogger := s.log.FromContext(ctx)
namespace, identifier := currentIdentity.GetNamespacedID()
if namespace != identity.NamespaceUser {
ctxLogger.Debug("Skipping default org sync, not a user", "namespace", namespace)
return nil
}
userID, err := identity.IntIdentifier(namespace, identifier)
if err != nil {
ctxLogger.Debug("Skipping default org sync, invalid ID for identity", "id", currentIdentity.ID, "namespace", namespace, "err", err)
return nil
}
hasAssignedToOrg, err := s.validateUsingOrg(ctx, userID, s.cfg.LoginDefaultOrgId)
if err != nil {
ctxLogger.Error("Skipping default org sync, failed to validate user's organizations", "id", currentIdentity.ID, "err", err)
return nil
}
if !hasAssignedToOrg {
ctxLogger.Debug("Skipping default org sync, user is not assigned to org", "id", currentIdentity.ID, "org", s.cfg.LoginDefaultOrgId)
return nil
}
cmd := user.SetUsingOrgCommand{UserID: userID, OrgID: s.cfg.LoginDefaultOrgId}
if err := s.userService.SetUsingOrg(ctx, &cmd); err != nil {
ctxLogger.Error("Failed to set default org", "id", currentIdentity.ID, "err", err)
return err
}
return nil
}
func (s *OrgSync) validateUsingOrg(ctx context.Context, userID int64, orgID int64) (bool, error) {
query := org.GetUserOrgListQuery{UserID: userID}
result, err := s.orgService.GetUserOrgList(ctx, &query)
if err != nil {
return false, fmt.Errorf("failed to get user's organizations: %w", err)
}
// validate that the org id in the list
for _, other := range result {
if other.OrgID == orgID {
return true, nil
}
}
return false, nil
}

View File

@ -2,9 +2,11 @@ package sync
import ( import (
"context" "context"
"fmt"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models/roletype" "github.com/grafana/grafana/pkg/models/roletype"
@ -16,6 +18,7 @@ import (
"github.com/grafana/grafana/pkg/services/org/orgtest" "github.com/grafana/grafana/pkg/services/org/orgtest"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/usertest" "github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/grafana/grafana/pkg/setting"
) )
func TestOrgSync_SyncOrgRolesHook(t *testing.T) { func TestOrgSync_SyncOrgRolesHook(t *testing.T) {
@ -124,3 +127,96 @@ func TestOrgSync_SyncOrgRolesHook(t *testing.T) {
}) })
} }
} }
func TestOrgSync_SetDefaultOrgHook(t *testing.T) {
testCases := []struct {
name string
defaultOrgSetting int64
identity *authn.Identity
setupMock func(*usertest.MockService, *orgtest.FakeOrgService)
wantErr bool
}{
{
name: "should set default org",
defaultOrgSetting: 2,
identity: &authn.Identity{ID: "user:1"},
setupMock: func(userService *usertest.MockService, orgService *orgtest.FakeOrgService) {
userService.On("SetUsingOrg", mock.Anything, mock.MatchedBy(func(cmd *user.SetUsingOrgCommand) bool {
return cmd.UserID == 1 && cmd.OrgID == 2
})).Return(nil)
},
},
{
name: "should skip setting the default org when default org is not set",
defaultOrgSetting: -1,
identity: &authn.Identity{ID: "user:1"},
},
{
name: "should skip setting the default org when identity is nil",
defaultOrgSetting: -1,
identity: nil,
},
{
name: "should skip setting the default org when identity is not a user",
defaultOrgSetting: 2,
identity: &authn.Identity{ID: "service-account:1"},
},
{
name: "should skip setting the default org when user id is not valid",
defaultOrgSetting: 2,
identity: &authn.Identity{ID: "user:invalid"},
},
{
name: "should skip setting the default org when user is not allowed to use the configured default org",
defaultOrgSetting: 3,
identity: &authn.Identity{ID: "user:1"},
},
{
name: "should skip setting the default org when validateUsingOrg returns error",
defaultOrgSetting: 2,
identity: &authn.Identity{ID: "user:1"},
setupMock: func(userService *usertest.MockService, orgService *orgtest.FakeOrgService) {
orgService.ExpectedError = fmt.Errorf("error")
},
},
{
name: "should return error when the user org update was unsuccessful",
defaultOrgSetting: 2,
identity: &authn.Identity{ID: "user:1"},
setupMock: func(userService *usertest.MockService, orgService *orgtest.FakeOrgService) {
userService.On("SetUsingOrg", mock.Anything, mock.Anything).Return(fmt.Errorf("error"))
},
wantErr: true,
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
cfg := setting.NewCfg()
cfg.LoginDefaultOrgId = tt.defaultOrgSetting
userService := &usertest.MockService{}
defer userService.AssertExpectations(t)
orgService := &orgtest.FakeOrgService{
ExpectedUserOrgDTO: []*org.UserOrgDTO{{OrgID: 2}},
}
if tt.setupMock != nil {
tt.setupMock(userService, orgService)
}
s := &OrgSync{
userService: userService,
orgService: orgService,
accessControl: actest.FakeService{},
log: log.NewNopLogger(),
cfg: cfg,
}
if err := s.SetDefaultOrgHook(context.Background(), tt.identity, nil); (err != nil) != tt.wantErr {
t.Errorf("OrgSync.SetDefaultOrgHook() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View File

@ -6,6 +6,7 @@ import (
"github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/registry"
) )
//go:generate mockery --name Service --structname MockService --outpkg usertest --filename mock.go --output ./usertest/
type Service interface { type Service interface {
registry.ProvidesUsageStats registry.ProvidesUsageStats
Create(context.Context, *CreateUserCommand) (*User, error) Create(context.Context, *CreateUserCommand) (*User, error)

View File

@ -0,0 +1,511 @@
// Code generated by mockery v2.40.1. DO NOT EDIT.
package usertest
import (
context "context"
user "github.com/grafana/grafana/pkg/services/user"
mock "github.com/stretchr/testify/mock"
)
// MockService is an autogenerated mock type for the Service type
type MockService struct {
mock.Mock
}
// BatchDisableUsers provides a mock function with given fields: _a0, _a1
func (_m *MockService) BatchDisableUsers(_a0 context.Context, _a1 *user.BatchDisableUsersCommand) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for BatchDisableUsers")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *user.BatchDisableUsersCommand) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// ChangePassword provides a mock function with given fields: _a0, _a1
func (_m *MockService) ChangePassword(_a0 context.Context, _a1 *user.ChangeUserPasswordCommand) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for ChangePassword")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *user.ChangeUserPasswordCommand) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// Create provides a mock function with given fields: _a0, _a1
func (_m *MockService) Create(_a0 context.Context, _a1 *user.CreateUserCommand) (*user.User, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for Create")
}
var r0 *user.User
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *user.CreateUserCommand) (*user.User, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *user.CreateUserCommand) *user.User); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*user.User)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *user.CreateUserCommand) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateServiceAccount provides a mock function with given fields: _a0, _a1
func (_m *MockService) CreateServiceAccount(_a0 context.Context, _a1 *user.CreateUserCommand) (*user.User, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for CreateServiceAccount")
}
var r0 *user.User
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *user.CreateUserCommand) (*user.User, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *user.CreateUserCommand) *user.User); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*user.User)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *user.CreateUserCommand) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Delete provides a mock function with given fields: _a0, _a1
func (_m *MockService) Delete(_a0 context.Context, _a1 *user.DeleteUserCommand) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *user.DeleteUserCommand) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// Disable provides a mock function with given fields: _a0, _a1
func (_m *MockService) Disable(_a0 context.Context, _a1 *user.DisableUserCommand) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for Disable")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *user.DisableUserCommand) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// GetByEmail provides a mock function with given fields: _a0, _a1
func (_m *MockService) GetByEmail(_a0 context.Context, _a1 *user.GetUserByEmailQuery) (*user.User, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for GetByEmail")
}
var r0 *user.User
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *user.GetUserByEmailQuery) (*user.User, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *user.GetUserByEmailQuery) *user.User); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*user.User)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *user.GetUserByEmailQuery) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetByID provides a mock function with given fields: _a0, _a1
func (_m *MockService) GetByID(_a0 context.Context, _a1 *user.GetUserByIDQuery) (*user.User, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for GetByID")
}
var r0 *user.User
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *user.GetUserByIDQuery) (*user.User, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *user.GetUserByIDQuery) *user.User); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*user.User)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *user.GetUserByIDQuery) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetByLogin provides a mock function with given fields: _a0, _a1
func (_m *MockService) GetByLogin(_a0 context.Context, _a1 *user.GetUserByLoginQuery) (*user.User, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for GetByLogin")
}
var r0 *user.User
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *user.GetUserByLoginQuery) (*user.User, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *user.GetUserByLoginQuery) *user.User); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*user.User)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *user.GetUserByLoginQuery) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetProfile provides a mock function with given fields: _a0, _a1
func (_m *MockService) GetProfile(_a0 context.Context, _a1 *user.GetUserProfileQuery) (*user.UserProfileDTO, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for GetProfile")
}
var r0 *user.UserProfileDTO
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *user.GetUserProfileQuery) (*user.UserProfileDTO, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *user.GetUserProfileQuery) *user.UserProfileDTO); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*user.UserProfileDTO)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *user.GetUserProfileQuery) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetSignedInUser provides a mock function with given fields: _a0, _a1
func (_m *MockService) GetSignedInUser(_a0 context.Context, _a1 *user.GetSignedInUserQuery) (*user.SignedInUser, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for GetSignedInUser")
}
var r0 *user.SignedInUser
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *user.GetSignedInUserQuery) (*user.SignedInUser, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *user.GetSignedInUserQuery) *user.SignedInUser); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*user.SignedInUser)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *user.GetSignedInUserQuery) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetSignedInUserWithCacheCtx provides a mock function with given fields: _a0, _a1
func (_m *MockService) GetSignedInUserWithCacheCtx(_a0 context.Context, _a1 *user.GetSignedInUserQuery) (*user.SignedInUser, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for GetSignedInUserWithCacheCtx")
}
var r0 *user.SignedInUser
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *user.GetSignedInUserQuery) (*user.SignedInUser, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *user.GetSignedInUserQuery) *user.SignedInUser); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*user.SignedInUser)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *user.GetSignedInUserQuery) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetUsageStats provides a mock function with given fields: ctx
func (_m *MockService) GetUsageStats(ctx context.Context) map[string]interface{} {
ret := _m.Called(ctx)
if len(ret) == 0 {
panic("no return value specified for GetUsageStats")
}
var r0 map[string]interface{}
if rf, ok := ret.Get(0).(func(context.Context) map[string]interface{}); ok {
r0 = rf(ctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]interface{})
}
}
return r0
}
// NewAnonymousSignedInUser provides a mock function with given fields: _a0
func (_m *MockService) NewAnonymousSignedInUser(_a0 context.Context) (*user.SignedInUser, error) {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for NewAnonymousSignedInUser")
}
var r0 *user.SignedInUser
var r1 error
if rf, ok := ret.Get(0).(func(context.Context) (*user.SignedInUser, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(context.Context) *user.SignedInUser); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*user.SignedInUser)
}
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Search provides a mock function with given fields: _a0, _a1
func (_m *MockService) Search(_a0 context.Context, _a1 *user.SearchUsersQuery) (*user.SearchUserQueryResult, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for Search")
}
var r0 *user.SearchUserQueryResult
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *user.SearchUsersQuery) (*user.SearchUserQueryResult, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *user.SearchUsersQuery) *user.SearchUserQueryResult); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*user.SearchUserQueryResult)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *user.SearchUsersQuery) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SetUserHelpFlag provides a mock function with given fields: _a0, _a1
func (_m *MockService) SetUserHelpFlag(_a0 context.Context, _a1 *user.SetUserHelpFlagCommand) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for SetUserHelpFlag")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *user.SetUserHelpFlagCommand) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// SetUsingOrg provides a mock function with given fields: _a0, _a1
func (_m *MockService) SetUsingOrg(_a0 context.Context, _a1 *user.SetUsingOrgCommand) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for SetUsingOrg")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *user.SetUsingOrgCommand) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// Update provides a mock function with given fields: _a0, _a1
func (_m *MockService) Update(_a0 context.Context, _a1 *user.UpdateUserCommand) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for Update")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *user.UpdateUserCommand) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// UpdateLastSeenAt provides a mock function with given fields: _a0, _a1
func (_m *MockService) UpdateLastSeenAt(_a0 context.Context, _a1 *user.UpdateUserLastSeenAtCommand) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for UpdateLastSeenAt")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *user.UpdateUserLastSeenAtCommand) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// UpdatePermissions provides a mock function with given fields: _a0, _a1, _a2
func (_m *MockService) UpdatePermissions(_a0 context.Context, _a1 int64, _a2 bool) error {
ret := _m.Called(_a0, _a1, _a2)
if len(ret) == 0 {
panic("no return value specified for UpdatePermissions")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, bool) error); ok {
r0 = rf(_a0, _a1, _a2)
} else {
r0 = ret.Error(0)
}
return r0
}
// NewMockService creates a new instance of MockService. 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 NewMockService(t interface {
mock.TestingT
Cleanup(func())
}) *MockService {
mock := &MockService{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -411,6 +411,7 @@ type Cfg struct {
AutoAssignOrg bool AutoAssignOrg bool
AutoAssignOrgId int AutoAssignOrgId int
AutoAssignOrgRole string AutoAssignOrgRole string
LoginDefaultOrgId int64
OAuthSkipOrgRoleUpdateSync bool OAuthSkipOrgRoleUpdateSync bool
// ExpressionsEnabled specifies whether expressions are enabled. // ExpressionsEnabled specifies whether expressions are enabled.
@ -1658,6 +1659,7 @@ func readUserSettings(iniFile *ini.File, cfg *Cfg) error {
cfg.AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true) cfg.AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true)
cfg.AutoAssignOrg = users.Key("auto_assign_org").MustBool(true) cfg.AutoAssignOrg = users.Key("auto_assign_org").MustBool(true)
cfg.AutoAssignOrgId = users.Key("auto_assign_org_id").MustInt(1) cfg.AutoAssignOrgId = users.Key("auto_assign_org_id").MustInt(1)
cfg.LoginDefaultOrgId = users.Key("login_default_org_id").MustInt64(-1)
cfg.AutoAssignOrgRole = users.Key("auto_assign_org_role").In( cfg.AutoAssignOrgRole = users.Key("auto_assign_org_role").In(
string(roletype.RoleViewer), []string{ string(roletype.RoleViewer), []string{
string(roletype.RoleNone), string(roletype.RoleNone),