mirror of https://github.com/grafana/grafana
parent
1594ceeb6f
commit
1c5afa731f
@ -0,0 +1,157 @@ |
||||
package login |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"github.com/grafana/grafana/pkg/bus" |
||||
"github.com/grafana/grafana/pkg/log" |
||||
m "github.com/grafana/grafana/pkg/models" |
||||
"github.com/grafana/grafana/pkg/services/quota" |
||||
) |
||||
|
||||
func UpsertUser(ctx *m.ReqContext, cmd *m.UpsertUserCommand) error { |
||||
extUser := cmd.ExternalUser |
||||
|
||||
userQuery := m.GetUserByAuthInfoQuery{ |
||||
AuthModule: extUser.AuthModule, |
||||
AuthId: extUser.AuthId, |
||||
UserId: extUser.UserId, |
||||
Email: extUser.Email, |
||||
Login: extUser.Login, |
||||
} |
||||
err := bus.Dispatch(&userQuery) |
||||
if err != nil { |
||||
if err != m.ErrUserNotFound { |
||||
return err |
||||
} |
||||
|
||||
if !cmd.SignupAllowed { |
||||
log.Warn(fmt.Sprintf("Not allowing %s login, user not found in internal user database and allow signup = false", extUser.AuthModule)) |
||||
return ErrInvalidCredentials |
||||
} |
||||
|
||||
limitReached, err := quota.QuotaReached(ctx, "user") |
||||
if err != nil { |
||||
log.Warn("Error getting user quota", "err", err) |
||||
return ErrGettingUserQuota |
||||
} |
||||
if limitReached { |
||||
return ErrUsersQuotaReached |
||||
} |
||||
|
||||
cmd.User, err = createUser(extUser) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} else { |
||||
cmd.User = userQuery.User |
||||
|
||||
// sync user info
|
||||
err = updateUser(cmd.User, extUser) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
err = syncOrgRoles(cmd.User, extUser) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func createUser(extUser *m.ExternalUserInfo) (*m.User, error) { |
||||
cmd := m.CreateUserCommand{ |
||||
Login: extUser.Login, |
||||
Email: extUser.Email, |
||||
Name: extUser.Name, |
||||
} |
||||
if err := bus.Dispatch(&cmd); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
cmd2 := m.SetAuthInfoCommand{ |
||||
UserId: cmd.Result.Id, |
||||
AuthModule: extUser.AuthModule, |
||||
AuthId: extUser.AuthId, |
||||
} |
||||
if err := bus.Dispatch(&cmd2); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return &cmd.Result, nil |
||||
} |
||||
|
||||
func updateUser(user *m.User, extUser *m.ExternalUserInfo) error { |
||||
// sync user info
|
||||
if user.Login != extUser.Login || user.Email != extUser.Email || user.Name != extUser.Name { |
||||
log.Debug("Syncing user info", "id", user.Id, "login", extUser.Login, "email", extUser.Email) |
||||
updateCmd := m.UpdateUserCommand{ |
||||
UserId: user.Id, |
||||
Login: extUser.Login, |
||||
Email: extUser.Email, |
||||
Name: extUser.Name, |
||||
} |
||||
err := bus.Dispatch(&updateCmd) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func syncOrgRoles(user *m.User, extUser *m.ExternalUserInfo) error { |
||||
if len(extUser.OrgRoles) == 0 { |
||||
// log.Warn("No group mappings defined")
|
||||
return nil |
||||
} |
||||
|
||||
orgsQuery := m.GetUserOrgListQuery{UserId: user.Id} |
||||
if err := bus.Dispatch(&orgsQuery); err != nil { |
||||
return err |
||||
} |
||||
|
||||
handledOrgIds := map[int64]bool{} |
||||
deleteOrgIds := []int64{} |
||||
|
||||
// update existing org roles
|
||||
for _, org := range orgsQuery.Result { |
||||
handledOrgIds[org.OrgId] = true |
||||
|
||||
if extUser.OrgRoles[org.OrgId] == "" { |
||||
deleteOrgIds = append(deleteOrgIds, org.OrgId) |
||||
} else if extUser.OrgRoles[org.OrgId] != org.Role { |
||||
// update role
|
||||
cmd := m.UpdateOrgUserCommand{OrgId: org.OrgId, UserId: user.Id, Role: extUser.OrgRoles[org.OrgId]} |
||||
if err := bus.Dispatch(&cmd); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
} |
||||
|
||||
// add any new org roles
|
||||
for orgId, orgRole := range extUser.OrgRoles { |
||||
if _, exists := handledOrgIds[orgId]; exists { |
||||
continue |
||||
} |
||||
|
||||
// add role
|
||||
cmd := m.AddOrgUserCommand{UserId: user.Id, Role: orgRole, OrgId: orgId} |
||||
err := bus.Dispatch(&cmd) |
||||
if err != nil && err != m.ErrOrgNotFound { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
// delete any removed org roles
|
||||
for _, orgId := range deleteOrgIds { |
||||
cmd := m.RemoveOrgUserCommand{OrgId: orgId, UserId: user.Id} |
||||
if err := bus.Dispatch(&cmd); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
@ -0,0 +1,66 @@ |
||||
package models |
||||
|
||||
type UserAuth struct { |
||||
Id int64 |
||||
UserId int64 |
||||
AuthModule string |
||||
AuthId string |
||||
} |
||||
|
||||
type ExternalUserInfo struct { |
||||
AuthModule string |
||||
AuthId string |
||||
UserId int64 |
||||
Email string |
||||
Login string |
||||
Name string |
||||
OrgRoles map[int64]RoleType |
||||
} |
||||
|
||||
// ---------------------
|
||||
// COMMANDS
|
||||
|
||||
type UpsertUserCommand struct { |
||||
ExternalUser *ExternalUserInfo |
||||
SignupAllowed bool |
||||
|
||||
User *User |
||||
} |
||||
|
||||
type SetAuthInfoCommand struct { |
||||
AuthModule string |
||||
AuthId string |
||||
UserId int64 |
||||
} |
||||
|
||||
type DeleteAuthInfoCommand struct { |
||||
UserAuth *UserAuth |
||||
} |
||||
|
||||
// ----------------------
|
||||
// QUERIES
|
||||
|
||||
type LoginUserQuery struct { |
||||
Username string |
||||
Password string |
||||
User *User |
||||
IpAddress string |
||||
} |
||||
|
||||
type GetUserByAuthInfoQuery struct { |
||||
AuthModule string |
||||
AuthId string |
||||
UserId int64 |
||||
Email string |
||||
Login string |
||||
|
||||
User *User |
||||
UserAuth *UserAuth |
||||
} |
||||
|
||||
type GetAuthInfoQuery struct { |
||||
AuthModule string |
||||
AuthId string |
||||
|
||||
UserAuth *UserAuth |
||||
} |
||||
@ -0,0 +1,24 @@ |
||||
package migrations |
||||
|
||||
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator" |
||||
|
||||
func addUserAuthMigrations(mg *Migrator) { |
||||
userAuthV1 := Table{ |
||||
Name: "user_auth", |
||||
Columns: []*Column{ |
||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, |
||||
{Name: "user_id", Type: DB_BigInt, Nullable: false}, |
||||
{Name: "auth_module", Type: DB_NVarchar, Length: 30, Nullable: false}, |
||||
{Name: "auth_id", Type: DB_NVarchar, Length: 100, Nullable: false}, |
||||
{Name: "created", Type: DB_DateTime, Nullable: false}, |
||||
}, |
||||
Indices: []*Index{ |
||||
{Cols: []string{"auth_module", "auth_id"}}, |
||||
}, |
||||
} |
||||
|
||||
// create table
|
||||
mg.AddMigration("create user auth table", NewAddTableMigration(userAuthV1)) |
||||
// add indices
|
||||
addTableIndicesMigrations(mg, "v1", userAuthV1) |
||||
} |
||||
@ -0,0 +1,130 @@ |
||||
package sqlstore |
||||
|
||||
import ( |
||||
"github.com/grafana/grafana/pkg/bus" |
||||
m "github.com/grafana/grafana/pkg/models" |
||||
) |
||||
|
||||
func init() { |
||||
bus.AddHandler("sql", GetUserByAuthInfo) |
||||
bus.AddHandler("sql", GetAuthInfo) |
||||
bus.AddHandler("sql", SetAuthInfo) |
||||
bus.AddHandler("sql", DeleteAuthInfo) |
||||
} |
||||
|
||||
func GetUserByAuthInfo(query *m.GetUserByAuthInfoQuery) error { |
||||
user := new(m.User) |
||||
has := false |
||||
var err error |
||||
|
||||
// Try to find the user by auth module and id first
|
||||
if query.AuthModule != "" && query.AuthId != "" { |
||||
authQuery := &m.GetAuthInfoQuery{ |
||||
AuthModule: query.AuthModule, |
||||
AuthId: query.AuthId, |
||||
} |
||||
|
||||
err = GetAuthInfo(authQuery) |
||||
// if user id was specified and doesn't match the user_auth entry, remove it
|
||||
if err == nil && query.UserId != 0 && query.UserId != authQuery.UserAuth.UserId { |
||||
DeleteAuthInfo(&m.DeleteAuthInfoCommand{ |
||||
UserAuth: authQuery.UserAuth, |
||||
}) |
||||
} else if err == nil { |
||||
has, err = x.Id(authQuery.UserAuth.UserId).Get(user) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
if has { |
||||
query.UserAuth = authQuery.UserAuth |
||||
} else { |
||||
// if the user has been deleted then remove the entry
|
||||
DeleteAuthInfo(&m.DeleteAuthInfoCommand{ |
||||
UserAuth: authQuery.UserAuth, |
||||
}) |
||||
} |
||||
} else if err != m.ErrUserNotFound { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
// If not found, try to find the user by id
|
||||
if !has && query.UserId != 0 { |
||||
has, err = x.Id(query.UserId).Get(user) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
// If not found, try to find the user by email address
|
||||
if !has && query.Email != "" { |
||||
user = &m.User{Email: query.Email} |
||||
has, err = x.Get(user) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
// If not found, try to find the user by login
|
||||
if !has && query.Login != "" { |
||||
user = &m.User{Login: query.Login} |
||||
has, err = x.Get(user) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
// No user found
|
||||
if !has { |
||||
return m.ErrUserNotFound |
||||
} |
||||
|
||||
query.User = user |
||||
return nil |
||||
} |
||||
|
||||
func GetAuthInfo(query *m.GetAuthInfoQuery) error { |
||||
userAuth := &m.UserAuth{ |
||||
AuthModule: query.AuthModule, |
||||
AuthId: query.AuthId, |
||||
} |
||||
has, err := x.Get(userAuth) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if !has { |
||||
return m.ErrUserNotFound |
||||
} |
||||
|
||||
query.UserAuth = userAuth |
||||
return nil |
||||
} |
||||
|
||||
func SetAuthInfo(cmd *m.SetAuthInfoCommand) error { |
||||
return inTransaction(func(sess *DBSession) error { |
||||
authUser := m.UserAuth{ |
||||
UserId: cmd.UserId, |
||||
AuthModule: cmd.AuthModule, |
||||
AuthId: cmd.AuthId, |
||||
} |
||||
|
||||
_, err := sess.Insert(&authUser) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
return nil |
||||
}) |
||||
} |
||||
|
||||
func DeleteAuthInfo(cmd *m.DeleteAuthInfoCommand) error { |
||||
return inTransaction(func(sess *DBSession) error { |
||||
_, err := sess.Delete(cmd.UserAuth) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
return nil |
||||
}) |
||||
} |
||||
Loading…
Reference in new issue