mirror of https://github.com/coder/coder.git
feat: use default org for PostUser (#12143)
Instead of assuming only 1 org exists, this uses the is_default org to place a user in if not specified.
This commit is contained in:
parent
0e1bad4f82
commit
2a8004b1b2
|
@ -1016,6 +1016,12 @@ func (q *querier) GetDERPMeshKey(ctx context.Context) (string, error) {
|
|||
return q.db.GetDERPMeshKey(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetDefaultOrganization(ctx context.Context) (database.Organization, error) {
|
||||
return fetch(q.log, q.auth, func(ctx context.Context, _ any) (database.Organization, error) {
|
||||
return q.db.GetDefaultOrganization(ctx)
|
||||
})(ctx, nil)
|
||||
}
|
||||
|
||||
func (q *querier) GetDefaultProxyConfig(ctx context.Context) (database.GetDefaultProxyConfigRow, error) {
|
||||
// No authz checks
|
||||
return q.db.GetDefaultProxyConfig(ctx)
|
||||
|
|
|
@ -570,6 +570,10 @@ func (s *MethodTestSuite) TestOrganization() {
|
|||
o := dbgen.Organization(s.T(), db, database.Organization{})
|
||||
check.Args(o.ID).Asserts(o, rbac.ActionRead).Returns(o)
|
||||
}))
|
||||
s.Run("GetDefaultOrganization", s.Subtest(func(db database.Store, check *expects) {
|
||||
o := dbgen.Organization(s.T(), db, database.Organization{})
|
||||
check.Args().Asserts(o, rbac.ActionRead).Returns(o)
|
||||
}))
|
||||
s.Run("GetOrganizationByName", s.Subtest(func(db database.Store, check *expects) {
|
||||
o := dbgen.Organization(s.T(), db, database.Organization{})
|
||||
check.Args(o.Name).Asserts(o, rbac.ActionRead).Returns(o)
|
||||
|
|
|
@ -1657,6 +1657,18 @@ func (q *FakeQuerier) GetDERPMeshKey(_ context.Context) (string, error) {
|
|||
return q.derpMeshKey, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetDefaultOrganization(_ context.Context) (database.Organization, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
for _, org := range q.organizations {
|
||||
if org.IsDefault {
|
||||
return org, nil
|
||||
}
|
||||
}
|
||||
return database.Organization{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetDefaultProxyConfig(_ context.Context) (database.GetDefaultProxyConfigRow, error) {
|
||||
return database.GetDefaultProxyConfigRow{
|
||||
DisplayName: q.defaultProxyDisplayName,
|
||||
|
|
|
@ -433,6 +433,13 @@ func (m metricsStore) GetDERPMeshKey(ctx context.Context) (string, error) {
|
|||
return key, err
|
||||
}
|
||||
|
||||
func (m metricsStore) GetDefaultOrganization(ctx context.Context) (database.Organization, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetDefaultOrganization(ctx)
|
||||
m.queryLatencies.WithLabelValues("GetDefaultOrganization").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetDefaultProxyConfig(ctx context.Context) (database.GetDefaultProxyConfigRow, error) {
|
||||
start := time.Now()
|
||||
resp, err := m.s.GetDefaultProxyConfig(ctx)
|
||||
|
|
|
@ -828,6 +828,21 @@ func (mr *MockStoreMockRecorder) GetDERPMeshKey(arg0 any) *gomock.Call {
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDERPMeshKey", reflect.TypeOf((*MockStore)(nil).GetDERPMeshKey), arg0)
|
||||
}
|
||||
|
||||
// GetDefaultOrganization mocks base method.
|
||||
func (m *MockStore) GetDefaultOrganization(arg0 context.Context) (database.Organization, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetDefaultOrganization", arg0)
|
||||
ret0, _ := ret[0].(database.Organization)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetDefaultOrganization indicates an expected call of GetDefaultOrganization.
|
||||
func (mr *MockStoreMockRecorder) GetDefaultOrganization(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultOrganization", reflect.TypeOf((*MockStore)(nil).GetDefaultOrganization), arg0)
|
||||
}
|
||||
|
||||
// GetDefaultProxyConfig mocks base method.
|
||||
func (m *MockStore) GetDefaultProxyConfig(arg0 context.Context) (database.GetDefaultProxyConfigRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
|
|
@ -102,6 +102,7 @@ type sqlcQuerier interface {
|
|||
GetAuthorizationUserRoles(ctx context.Context, userID uuid.UUID) (GetAuthorizationUserRolesRow, error)
|
||||
GetDBCryptKeys(ctx context.Context) ([]DBCryptKey, error)
|
||||
GetDERPMeshKey(ctx context.Context) (string, error)
|
||||
GetDefaultOrganization(ctx context.Context) (Organization, error)
|
||||
GetDefaultProxyConfig(ctx context.Context) (GetDefaultProxyConfigRow, error)
|
||||
GetDeploymentDAUs(ctx context.Context, tzOffset int32) ([]GetDeploymentDAUsRow, error)
|
||||
GetDeploymentID(ctx context.Context) (string, error)
|
||||
|
|
|
@ -3142,6 +3142,31 @@ func (q *sqlQuerier) UpdateMemberRoles(ctx context.Context, arg UpdateMemberRole
|
|||
return i, err
|
||||
}
|
||||
|
||||
const getDefaultOrganization = `-- name: GetDefaultOrganization :one
|
||||
SELECT
|
||||
id, name, description, created_at, updated_at, is_default
|
||||
FROM
|
||||
organizations
|
||||
WHERE
|
||||
is_default = true
|
||||
LIMIT
|
||||
1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetDefaultOrganization(ctx context.Context) (Organization, error) {
|
||||
row := q.db.QueryRowContext(ctx, getDefaultOrganization)
|
||||
var i Organization
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.IsDefault,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getOrganizationByID = `-- name: GetOrganizationByID :one
|
||||
SELECT
|
||||
id, name, description, created_at, updated_at, is_default
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
-- name: GetDefaultOrganization :one
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
organizations
|
||||
WHERE
|
||||
is_default = true
|
||||
LIMIT
|
||||
1;
|
||||
|
||||
-- name: GetOrganizations :many
|
||||
SELECT
|
||||
*
|
||||
|
|
|
@ -401,10 +401,18 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
} else {
|
||||
// If no organization is provided, add the user to the first
|
||||
// organization.
|
||||
organizations, err := api.Database.GetOrganizations(ctx)
|
||||
// If no organization is provided, add the user to the default
|
||||
defaultOrg, err := api.Database.GetDefaultOrganization(ctx)
|
||||
if err != nil {
|
||||
if httpapi.Is404Error(err) {
|
||||
httpapi.Write(ctx, rw, http.StatusNotFound,
|
||||
codersdk.Response{
|
||||
Message: "Resource not found or you do not have access to this resource",
|
||||
Detail: "Organization not found",
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching orgs.",
|
||||
Detail: err.Error(),
|
||||
|
@ -412,12 +420,7 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if len(organizations) > 0 {
|
||||
// Add the user to the first organization. Once multi-organization
|
||||
// support is added, we should enable a configuration map of user
|
||||
// email to organization.
|
||||
req.OrganizationID = organizations[0].ID
|
||||
}
|
||||
req.OrganizationID = defaultOrg.ID
|
||||
}
|
||||
|
||||
var loginType database.LoginType
|
||||
|
|
|
@ -493,21 +493,26 @@ func TestPostUsers(t *testing.T) {
|
|||
t.Parallel()
|
||||
auditor := audit.NewMock()
|
||||
client := coderdtest.New(t, &coderdtest.Options{Auditor: auditor})
|
||||
numLogs := len(auditor.AuditLogs())
|
||||
|
||||
firstUser := coderdtest.CreateFirstUser(t, client)
|
||||
numLogs++ // add an audit log for user create
|
||||
numLogs++ // add an audit log for login
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
// Add an extra org to try and confuse user creation
|
||||
_, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
|
||||
Name: "foobar",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
numLogs := len(auditor.AuditLogs())
|
||||
|
||||
user, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
|
||||
Email: "another@user.org",
|
||||
Username: "someone-else",
|
||||
Password: "SomeSecurePassword!",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
numLogs++ // add an audit log for user create
|
||||
|
||||
require.Len(t, auditor.AuditLogs(), numLogs)
|
||||
require.Equal(t, database.AuditActionCreate, auditor.AuditLogs()[numLogs-1].Action)
|
||||
|
|
Loading…
Reference in New Issue