mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Allow API to assign new user to a specific organization (#21775)
* Allow API to assign new user to a specific organization * Add defer block to test * Add API tests and return 400 instead of 500 for bad orgId * Minor test improvements
This commit is contained in:
parent
da6056d5e1
commit
d721dd13cd
@ -224,10 +224,13 @@ Content-Type: application/json
|
||||
"name":"User",
|
||||
"email":"user@graf.com",
|
||||
"login":"user",
|
||||
"password":"userpassword"
|
||||
"password":"userpassword",
|
||||
"OrgId": 1
|
||||
}
|
||||
```
|
||||
|
||||
Note that `OrgId` is an optional parameter that can be used to assign a new user to a different organization when [auto_assign_org](https://grafana.com/docs/grafana/latest/installation/configuration/#auto-assign-org) is set to `true`.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
|
@ -14,6 +14,7 @@ func AdminCreateUser(c *models.ReqContext, form dtos.AdminCreateUserForm) {
|
||||
Email: form.Email,
|
||||
Password: form.Password,
|
||||
Name: form.Name,
|
||||
OrgId: form.OrgId,
|
||||
}
|
||||
|
||||
if len(cmd.Login) == 0 {
|
||||
@ -30,6 +31,11 @@ func AdminCreateUser(c *models.ReqContext, form dtos.AdminCreateUserForm) {
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
if err == models.ErrOrgNotFound {
|
||||
c.JsonApiErr(400, models.ErrOrgNotFound.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonApiErr(500, "failed to create user", err)
|
||||
return
|
||||
}
|
||||
|
@ -12,6 +12,12 @@ import (
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
const (
|
||||
TestLogin = "test@example.com"
|
||||
TestPassword = "password"
|
||||
nonExistingOrgID = 1000
|
||||
)
|
||||
|
||||
func TestAdminApiEndpoint(t *testing.T) {
|
||||
role := models.ROLE_ADMIN
|
||||
Convey("Given a server admin attempts to remove themself as an admin", t, func() {
|
||||
@ -175,6 +181,85 @@ func TestAdminApiEndpoint(t *testing.T) {
|
||||
So(userId, ShouldEqual, 42)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When a server admin attempts to create a user", t, func() {
|
||||
var userLogin string
|
||||
var orgId int64
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.CreateUserCommand) error {
|
||||
userLogin = cmd.Login
|
||||
orgId = cmd.OrgId
|
||||
|
||||
if orgId == nonExistingOrgID {
|
||||
return models.ErrOrgNotFound
|
||||
}
|
||||
|
||||
cmd.Result = models.User{Id: TestUserID}
|
||||
return nil
|
||||
})
|
||||
|
||||
Convey("Without an organization", func() {
|
||||
createCmd := dtos.AdminCreateUserForm{
|
||||
Login: TestLogin,
|
||||
Password: TestPassword,
|
||||
}
|
||||
|
||||
adminCreateUserScenario("Should create the user", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
|
||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||
So(sc.resp.Code, ShouldEqual, 200)
|
||||
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
So(err, ShouldBeNil)
|
||||
So(respJSON.Get("id").MustInt64(), ShouldEqual, TestUserID)
|
||||
So(respJSON.Get("message").MustString(), ShouldEqual, "User created")
|
||||
|
||||
// test that userLogin and orgId were transmitted correctly to the handler
|
||||
So(userLogin, ShouldEqual, TestLogin)
|
||||
So(orgId, ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With an organization", func() {
|
||||
createCmd := dtos.AdminCreateUserForm{
|
||||
Login: TestLogin,
|
||||
Password: TestPassword,
|
||||
OrgId: TestOrgID,
|
||||
}
|
||||
|
||||
adminCreateUserScenario("Should create the user", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
|
||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||
So(sc.resp.Code, ShouldEqual, 200)
|
||||
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
So(err, ShouldBeNil)
|
||||
So(respJSON.Get("id").MustInt64(), ShouldEqual, TestUserID)
|
||||
So(respJSON.Get("message").MustString(), ShouldEqual, "User created")
|
||||
|
||||
So(userLogin, ShouldEqual, TestLogin)
|
||||
So(orgId, ShouldEqual, TestOrgID)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With a nonexistent organization", func() {
|
||||
createCmd := dtos.AdminCreateUserForm{
|
||||
Login: TestLogin,
|
||||
Password: TestPassword,
|
||||
OrgId: nonExistingOrgID,
|
||||
}
|
||||
|
||||
adminCreateUserScenario("Should create the user", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
|
||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||
So(sc.resp.Code, ShouldEqual, 400)
|
||||
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
So(err, ShouldBeNil)
|
||||
So(respJSON.Get("message").MustString(), ShouldEqual, "Organization not found")
|
||||
|
||||
So(userLogin, ShouldEqual, TestLogin)
|
||||
So(orgId, ShouldEqual, 1000)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func putAdminScenario(desc string, url string, routePattern string, role models.RoleType, cmd dtos.AdminUpdateUserPermissionsForm, fn scenarioFunc) {
|
||||
@ -324,3 +409,21 @@ func adminDeleteUserScenario(desc string, url string, routePattern string, fn sc
|
||||
fn(sc)
|
||||
})
|
||||
}
|
||||
|
||||
func adminCreateUserScenario(desc string, url string, routePattern string, cmd dtos.AdminCreateUserForm, fn scenarioFunc) {
|
||||
Convey(desc+" "+url, func() {
|
||||
defer bus.ClearBusHandlers()
|
||||
|
||||
sc := setupScenarioContext(url)
|
||||
sc.defaultHandler = Wrap(func(c *models.ReqContext) {
|
||||
sc.context = c
|
||||
sc.context.UserId = TestUserID
|
||||
|
||||
AdminCreateUser(c, cmd)
|
||||
})
|
||||
|
||||
sc.m.Post(routePattern, sc.defaultHandler)
|
||||
|
||||
fn(sc)
|
||||
})
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ type AdminCreateUserForm struct {
|
||||
Login string `json:"login"`
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password" binding:"Required"`
|
||||
OrgId int64 `json:"orgId"`
|
||||
}
|
||||
|
||||
type AdminUpdateUserForm struct {
|
||||
|
@ -58,6 +58,7 @@ type CreateUserCommand struct {
|
||||
Login string
|
||||
Name string
|
||||
Company string
|
||||
OrgId int64
|
||||
OrgName string
|
||||
Password string
|
||||
EmailVerified bool
|
||||
|
@ -220,6 +220,18 @@ func DeleteOrg(cmd *models.DeleteOrgCommand) error {
|
||||
})
|
||||
}
|
||||
|
||||
func verifyExistingOrg(sess *DBSession, orgId int64) error {
|
||||
var org models.Org
|
||||
has, err := sess.Where("id=?", orgId).Get(&org)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !has {
|
||||
return models.ErrOrgNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOrCreateOrg(sess *DBSession, orgName string) (int64, error) {
|
||||
var org models.Org
|
||||
|
||||
|
@ -41,6 +41,14 @@ func getOrgIdForNewUser(sess *DBSession, cmd *models.CreateUserCommand) (int64,
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
if setting.AutoAssignOrg && cmd.OrgId != 0 {
|
||||
err := verifyExistingOrg(sess, cmd.OrgId)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return cmd.OrgId, nil
|
||||
}
|
||||
|
||||
orgName := cmd.OrgName
|
||||
if len(orgName) == 0 {
|
||||
orgName = util.StringsFallback2(cmd.Email, cmd.Login)
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
@ -63,6 +65,56 @@ func TestUserDataAccess(t *testing.T) {
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given an organization", func() {
|
||||
autoAssignOrg := setting.AutoAssignOrg
|
||||
setting.AutoAssignOrg = true
|
||||
defer func() {
|
||||
setting.AutoAssignOrg = autoAssignOrg
|
||||
}()
|
||||
|
||||
orgCmd := &models.CreateOrgCommand{Name: "Some Test Org"}
|
||||
err := CreateOrg(orgCmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Creates user assigned to other organization", func() {
|
||||
cmd := &models.CreateUserCommand{
|
||||
Email: "usertest@test.com",
|
||||
Name: "user name",
|
||||
Login: "user_test_login",
|
||||
OrgId: orgCmd.Result.Id,
|
||||
}
|
||||
|
||||
err := CreateUser(context.Background(), cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Loading a user", func() {
|
||||
query := models.GetUserByIdQuery{Id: cmd.Result.Id}
|
||||
err := GetUserById(&query)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(query.Result.Email, ShouldEqual, "usertest@test.com")
|
||||
So(query.Result.Password, ShouldEqual, "")
|
||||
So(query.Result.Rands, ShouldHaveLength, 10)
|
||||
So(query.Result.Salt, ShouldHaveLength, 10)
|
||||
So(query.Result.IsDisabled, ShouldBeFalse)
|
||||
So(query.Result.OrgId, ShouldEqual, orgCmd.Result.Id)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Don't create user assigned to unknown organization", func() {
|
||||
const nonExistingOrgID = 10000
|
||||
cmd := &models.CreateUserCommand{
|
||||
Email: "usertest@test.com",
|
||||
Name: "user name",
|
||||
Login: "user_test_login",
|
||||
OrgId: nonExistingOrgID,
|
||||
}
|
||||
|
||||
err := CreateUser(context.Background(), cmd)
|
||||
So(err, ShouldEqual, models.ErrOrgNotFound)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given 5 users", func() {
|
||||
users := createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
||||
return &models.CreateUserCommand{
|
||||
|
Loading…
Reference in New Issue
Block a user