diff --git a/auth.go b/auth.go index f1880ed5df5..351452a2fcb 100644 --- a/auth.go +++ b/auth.go @@ -20,7 +20,7 @@ type Authorization struct { Status Status `json:"status"` Description string `json:"description"` OrgID ID `json:"orgID"` - UserID ID `json:"userID"` + UserID ID `json:"userID,omitempty"` Permissions []Permission `json:"permissions"` } diff --git a/cmd/influx/authorization.go b/cmd/influx/authorization.go index 6de8540da18..8c2534b164c 100644 --- a/cmd/influx/authorization.go +++ b/cmd/influx/authorization.go @@ -60,8 +60,7 @@ func init() { authorizationCreateCmd.Flags().StringVarP(&authorizationCreateFlags.org, "org", "o", "", "org name (required)") authorizationCreateCmd.MarkFlagRequired("org") - authorizationCreateCmd.Flags().StringVarP(&authorizationCreateFlags.user, "user", "u", "", "user name (required)") - authorizationCreateCmd.MarkFlagRequired("user") + authorizationCreateCmd.Flags().StringVarP(&authorizationCreateFlags.user, "user", "u", "", "user name") authorizationCreateCmd.Flags().BoolVarP(&authorizationCreateFlags.writeUserPermission, "write-user", "", false, "grants the permission to perform mutative actions against organization users") authorizationCreateCmd.Flags().BoolVarP(&authorizationCreateFlags.readUserPermission, "read-user", "", false, "grants the permission to perform read actions against organization users") @@ -229,6 +228,23 @@ func authorizationCreateF(cmd *cobra.Command, args []string) error { OrgID: o.ID, } + if authorizationCreateFlags.user != "" { + // if the user flag is supplied, then set the user ID explicitly on the request + userSvc, err := newUserService(flags) + if err != nil { + return err + } + userFilter := platform.UserFilter{ + Name: &authorizationCreateFlags.user, + } + user, err := userSvc.FindUser(context.Background(), userFilter) + if err != nil { + return err + } + + authorization.UserID = user.ID + } + s, err := newAuthorizationService(flags) if err != nil { return err diff --git a/http/auth_service.go b/http/auth_service.go index 8ced3fa8180..0af2b1e8d7a 100644 --- a/http/auth_service.go +++ b/http/auth_service.go @@ -141,10 +141,25 @@ func (h *AuthorizationHandler) handlePostAuthorization(w http.ResponseWriter, r return } - user, err := getAuthorizedUser(r, h.UserService) - if err != nil { - EncodeError(ctx, platform.ErrUnableToCreateToken, w) - return + var user *platform.User + // allow the user id to be specified optionally, if it is not set + // we use the id from the authorizer + if req.UserID == nil { + u, err := getAuthorizedUser(r, h.UserService) + if err != nil { + EncodeError(ctx, platform.ErrUnableToCreateToken, w) + return + } + + user = u + } else { + u, err := h.UserService.FindUserByID(ctx, *req.UserID) + if err != nil { + EncodeError(ctx, platform.ErrUnableToCreateToken, w) + return + } + + user = u } auth := req.toPlatform(user.ID) @@ -167,7 +182,7 @@ func (h *AuthorizationHandler) handlePostAuthorization(w http.ResponseWriter, r } if err := encodeResponse(ctx, w, http.StatusCreated, newAuthResponse(auth, org, user, perms)); err != nil { - EncodeError(ctx, err, w) + logEncodingError(h.Logger, r, err) return } } @@ -175,6 +190,7 @@ func (h *AuthorizationHandler) handlePostAuthorization(w http.ResponseWriter, r type postAuthorizationRequest struct { Status platform.Status `json:"status"` OrgID platform.ID `json:"orgID"` + UserID *platform.ID `json:"userID,omitempty"` Description string `json:"description"` Permissions []platform.Permission `json:"permissions"` } @@ -197,6 +213,10 @@ func newPostAuthorizationRequest(a *platform.Authorization) (*postAuthorizationR Status: a.Status, } + if a.UserID.Valid() { + res.UserID = &a.UserID + } + res.SetDefaults() return res, res.Validate() diff --git a/http/auth_test.go b/http/auth_test.go index 48337371c8b..a0b7e653cb2 100644 --- a/http/auth_test.go +++ b/http/auth_test.go @@ -13,7 +13,9 @@ import ( platform "github.com/influxdata/influxdb" pcontext "github.com/influxdata/influxdb/context" "github.com/influxdata/influxdb/inmem" + "github.com/influxdata/influxdb/kit/errors" "github.com/influxdata/influxdb/mock" + platformtesting "github.com/influxdata/influxdb/testing" "github.com/julienschmidt/httprouter" ) @@ -398,6 +400,9 @@ func TestService_handlePostAuthorization(t *testing.T) { }, UserService: &mock.UserService{ FindUserByIDFn: func(ctx context.Context, id platform.ID) (*platform.User, error) { + if !id.Valid() { + return nil, platform.ErrInvalidID + } return &platform.User{ ID: id, Name: "u1", @@ -406,6 +411,9 @@ func TestService_handlePostAuthorization(t *testing.T) { }, OrganizationService: &mock.OrganizationService{ FindOrganizationByIDF: func(ctx context.Context, id platform.ID) (*platform.Organization, error) { + if !id.Valid() { + return nil, platform.ErrInvalidID + } return &platform.Organization{ ID: id, Name: "o1", @@ -430,7 +438,6 @@ func TestService_handlePostAuthorization(t *testing.T) { }, authorization: &platform.Authorization{ ID: platformtesting.MustIDBase16("020f755c3c082000"), - UserID: platformtesting.MustIDBase16("aaaaaaaaaaaaaaaa"), OrgID: platformtesting.MustIDBase16("020f755c3c083000"), Description: "only read dashboards sucka", Permissions: []platform.Permission{ @@ -467,6 +474,95 @@ func TestService_handlePostAuthorization(t *testing.T) { "user": "u1", "userID": "aaaaaaaaaaaaaaaa" } +`, + }, + }, + { + name: "create a new authorization with user id set explicitly", + fields: fields{ + AuthorizationService: &mock.AuthorizationService{ + CreateAuthorizationFn: func(ctx context.Context, c *platform.Authorization) error { + c.ID = platformtesting.MustIDBase16("020f755c3c082000") + c.Token = "new-test-token" + return nil + }, + }, + UserService: &mock.UserService{ + FindUserByIDFn: func(ctx context.Context, id platform.ID) (*platform.User, error) { + if !id.Valid() { + return nil, errors.New("invalid user ID") + } + return &platform.User{ + ID: id, + Name: "u1", + }, nil + }, + }, + OrganizationService: &mock.OrganizationService{ + FindOrganizationByIDF: func(ctx context.Context, id platform.ID) (*platform.Organization, error) { + if !id.Valid() { + return nil, errors.New("invalid org ID") + } + return &platform.Organization{ + ID: id, + Name: "o1", + }, nil + }, + }, + }, + args: args{ + session: &platform.Authorization{ + Token: "session-token", + ID: platformtesting.MustIDBase16("020f755c3c082000"), + UserID: platformtesting.MustIDBase16("aaaaaaaaaaaaaaaa"), + OrgID: platformtesting.MustIDBase16("020f755c3c083000"), + Description: "can write to authorization resource", + Permissions: []platform.Permission{ + { + Action: platform.WriteAction, + Resource: platform.AuthorizationsResource, + }, + }, + }, + authorization: &platform.Authorization{ + ID: platformtesting.MustIDBase16("020f755c3c082000"), + UserID: platformtesting.MustIDBase16("bbbbbbbbbbbbbbbb"), + OrgID: platformtesting.MustIDBase16("020f755c3c083000"), + Description: "only read dashboards sucka", + Permissions: []platform.Permission{ + { + Action: platform.ReadAction, + Resource: platform.DashboardsResource, + OrgID: platformtesting.MustIDBase16("020f755c3c083000"), + }, + }, + }, + }, + wants: wants{ + statusCode: http.StatusCreated, + contentType: "application/json; charset=utf-8", + body: ` +{ + "links": { + "user": "/api/v2/users/bbbbbbbbbbbbbbbb", + "self": "/api/v2/authorizations/020f755c3c082000" + }, + "id": "020f755c3c082000", + "user": "u1", + "userID": "bbbbbbbbbbbbbbbb", + "orgID": "020f755c3c083000", + "org": "o1", + "token": "new-test-token", + "status": "active", + "description": "only read dashboards sucka", + "permissions": [ + { + "action": "read", + "orgID": "020f755c3c083000", + "resource": "dashboards" + } + ] +} `, }, }, @@ -479,7 +575,11 @@ func TestService_handlePostAuthorization(t *testing.T) { h.UserService = tt.fields.UserService h.OrganizationService = tt.fields.OrganizationService - b, err := json.Marshal(tt.args.authorization) + req, err := newPostAuthorizationRequest(tt.args.authorization) + if err != nil { + t.Fatalf("failed to create new authorization request: %v", err) + } + b, err := json.Marshal(req) if err != nil { t.Fatalf("failed to unmarshal authorization: %v", err) }