From fbff6cea76153e3faceffc27e416c2163081f9cd Mon Sep 17 00:00:00 2001 From: Michael Desa Date: Thu, 9 Nov 2017 16:31:56 -0500 Subject: [PATCH] Add user that made the request to organization Previously, when an org was created it had no users. Now when an organization is created, the user that made the request is added to the organization with the admin role. If there are any errors when adding the user the organization, we make a best effort attempt to delete the organization. However it is still possilbe that an organization would be created, but have no users if our best effort fails. --- server/organizations.go | 30 +++++++++ server/organizations_test.go | 114 +++++++++++++++++++++++++++++++++-- 2 files changed, 140 insertions(+), 4 deletions(-) diff --git a/server/organizations.go b/server/organizations.go index abac14776..dd1d200d1 100644 --- a/server/organizations.go +++ b/server/organizations.go @@ -1,6 +1,7 @@ package server import ( + "context" "encoding/json" "fmt" "net/http" @@ -8,6 +9,7 @@ import ( "github.com/bouk/httprouter" "github.com/influxdata/chronograf" + "github.com/influxdata/chronograf/organizations" "github.com/influxdata/chronograf/roles" ) @@ -123,6 +125,34 @@ func (s *Service) NewOrganization(w http.ResponseWriter, r *http.Request) { return } + // Now that the organization was created, add the user + // making the request to the organization + user, ok := hasUserContext(ctx) + if !ok { + // Best attempt at cleanup the organization if there were any errors + _ = s.Store.Organizations(ctx).Delete(ctx, res) + Error(w, http.StatusInternalServerError, "failed to retrieve user from context", s.Logger) + return + } + + orgID := fmt.Sprintf("%d", res.ID) + user.Roles = []chronograf.Role{ + { + Organization: orgID, + Name: roles.AdminRoleName, + }, + } + + orgCtx := context.WithValue(ctx, organizations.ContextKey, orgID) + _, err = s.Store.Users(orgCtx).Add(orgCtx, user) + if err != nil { + // Best attempt at cleanup the organization if there were any errors adding user to org + _ = s.Store.Organizations(ctx).Delete(ctx, res) + s.Logger.Error("failed to add user to organization", err.Error()) + Error(w, http.StatusInternalServerError, "failed to add user to organization", s.Logger) + return + } + co := newOrganizationResponse(res) location(w, co.Links.Self) encodeJSON(w, http.StatusCreated, co, s.Logger) diff --git a/server/organizations_test.go b/server/organizations_test.go index e223f8e70..6869eca82 100644 --- a/server/organizations_test.go +++ b/server/organizations_test.go @@ -352,12 +352,14 @@ func TestService_RemoveOrganization(t *testing.T) { func TestService_NewOrganization(t *testing.T) { type fields struct { OrganizationsStore chronograf.OrganizationsStore + UsersStore chronograf.UsersStore Logger chronograf.Logger } type args struct { - w *httptest.ResponseRecorder - r *http.Request - org *organizationRequest + w *httptest.ResponseRecorder + r *http.Request + org *organizationRequest + user *chronograf.User } tests := []struct { name string @@ -377,12 +379,28 @@ func TestService_NewOrganization(t *testing.T) { "http://any.url", // can be any valid URL as we are bypassing mux nil, ), + user: &chronograf.User{ + ID: 1, + Name: "bobetta", + Provider: "github", + Scheme: "oauth2", + }, org: &organizationRequest{ Name: "The Good Place", }, }, fields: fields{ Logger: log.New(log.DebugLevel), + UsersStore: &mocks.UsersStore{ + AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { + return &chronograf.User{ + ID: 1, + Name: "bobetta", + Provider: "github", + Scheme: "oauth2", + }, nil + }, + }, OrganizationsStore: &mocks.OrganizationsStore{ AddF: func(ctx context.Context, o *chronograf.Organization) (*chronograf.Organization, error) { return &chronograf.Organization{ @@ -396,17 +414,105 @@ func TestService_NewOrganization(t *testing.T) { wantContentType: "application/json", wantBody: `{"id":"1337","name":"The Good Place","links":{"self":"/chronograf/v1/organizations/1337"}}`, }, + { + name: "Create Organization - no user on context", + args: args{ + w: httptest.NewRecorder(), + r: httptest.NewRequest( + "GET", + "http://any.url", // can be any valid URL as we are bypassing mux + nil, + ), + org: &organizationRequest{ + Name: "The Good Place", + }, + }, + fields: fields{ + Logger: log.New(log.DebugLevel), + UsersStore: &mocks.UsersStore{ + AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { + return &chronograf.User{ + ID: 1, + Name: "bobetta", + Provider: "github", + Scheme: "oauth2", + }, nil + }, + }, + OrganizationsStore: &mocks.OrganizationsStore{ + AddF: func(ctx context.Context, o *chronograf.Organization) (*chronograf.Organization, error) { + return &chronograf.Organization{ + ID: 1337, + Name: "The Good Place", + }, nil + }, + DeleteF: func(ctx context.Context, o *chronograf.Organization) error { + return nil + }, + }, + }, + wantStatus: http.StatusInternalServerError, + wantContentType: "application/json", + wantBody: `{"code":500,"message":"failed to retrieve user from context"}`, + }, + { + name: "Create Organization - failed to add user to organization", + args: args{ + w: httptest.NewRecorder(), + r: httptest.NewRequest( + "GET", + "http://any.url", // can be any valid URL as we are bypassing mux + nil, + ), + org: &organizationRequest{ + Name: "The Good Place", + }, + user: &chronograf.User{ + ID: 1, + Name: "bobetta", + Provider: "github", + Scheme: "oauth2", + }, + }, + fields: fields{ + Logger: log.New(log.DebugLevel), + UsersStore: &mocks.UsersStore{ + AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { + return nil, fmt.Errorf("failed to add user to org") + }, + }, + OrganizationsStore: &mocks.OrganizationsStore{ + AddF: func(ctx context.Context, o *chronograf.Organization) (*chronograf.Organization, error) { + return &chronograf.Organization{ + ID: 1337, + Name: "The Good Place", + }, nil + }, + DeleteF: func(ctx context.Context, o *chronograf.Organization) error { + return nil + }, + }, + }, + wantStatus: http.StatusInternalServerError, + wantContentType: "application/json", + wantBody: `{"code":500,"message":"failed to add user to organization"}`, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := &Service{ - Store: &Store{ + Store: &mocks.Store{ OrganizationsStore: tt.fields.OrganizationsStore, + UsersStore: tt.fields.UsersStore, }, Logger: tt.fields.Logger, } + ctx := tt.args.r.Context() + ctx = context.WithValue(ctx, UserKey, tt.args.user) + tt.args.r = tt.args.r.WithContext(ctx) + buf, _ := json.Marshal(tt.args.org) tt.args.r.Body = ioutil.NopCloser(bytes.NewReader(buf)) s.NewOrganization(tt.args.w, tt.args.r)