diff --git a/api/authtokens/authtoken.gen.go b/api/authtokens/authtoken.gen.go new file mode 100644 index 000000000..dd5a5fa9d --- /dev/null +++ b/api/authtokens/authtoken.gen.go @@ -0,0 +1,78 @@ +// Code generated by "make api"; DO NOT EDIT. +package authtokens + +import ( + "encoding/json" + "strings" + "time" + + "github.com/fatih/structs" + + "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/internal/strutil" +) + +type AuthToken struct { + Client *api.Client `json:"-"` + + defaultFields []string + + // The ID of the AuthToken + // Output only. + Id string `json:"id,omitempty"` + // The token. This will only be populated after login and is only ever visible to the end user whose login + // request resulted in this auth token being created. + // Output only. + Token string `json:"token,omitempty"` + // The id of the user of this AuthToken. + // Output only. + UserId string `json:"user_id,omitempty"` + // The id of the user of this AuthToken. + // Output only. + AuthMethodId string `json:"auth_method_id,omitempty"` + // The time this resource was created + // Output only. + CreatedTime time.Time `json:"created_time,omitempty"` + // The time this resource was last updated. + // Output only. + UpdatedTime time.Time `json:"updated_time,omitempty"` + // The approximate time this AuthToken was last used. + // Output only. + ApproximateLastUsedTime time.Time `json:"approximate_last_used_time,omitempty"` + // The time this AuthToken expires. + // Output only. + ExpirationTime time.Time `json:"expiration_time,omitempty"` +} + +func (s *AuthToken) SetDefault(key string) { + lowerKey := strings.ToLower(key) + validMap := map[string]string{"approximatelastusedtime": "approximate_last_used_time", "authmethodid": "auth_method_id", "createdtime": "created_time", "expirationtime": "expiration_time", "id": "id", "token": "token", "updatedtime": "updated_time", "userid": "user_id"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } +} + +func (s *AuthToken) UnsetDefault(key string) { + lowerKey := strings.ToLower(key) + validMap := map[string]string{"approximatelastusedtime": "approximate_last_used_time", "authmethodid": "auth_method_id", "createdtime": "created_time", "expirationtime": "expiration_time", "id": "id", "token": "token", "updatedtime": "updated_time", "userid": "user_id"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } +} + +func (s AuthToken) MarshalJSON() ([]byte, error) { + m := structs.Map(s) + if m == nil { + m = make(map[string]interface{}) + } + for _, k := range s.defaultFields { + m[k] = nil + } + return json.Marshal(m) +} diff --git a/api/client.go b/api/client.go index 8fa9176bf..ab3de38b3 100644 --- a/api/client.go +++ b/api/client.go @@ -143,6 +143,7 @@ func DefaultConfig() (*Config, error) { Addr: "https://127.0.0.1:9200", HttpClient: cleanhttp.DefaultPooledClient(), Timeout: time.Second * 60, + TLSConfig: &TLSConfig{}, } // We read the environment now; after DefaultClient returns we can override @@ -217,10 +218,10 @@ func (c *Config) ConfigureTLS() error { return nil } -// setAddress parses a given string and looks for org and project info, setting -// the actual address to the base. Note that if a very malformed URL is passed -// in, this may not return what one expects. For now this is on purpose to -// avoid requiring error handling. +// setAddr parses a given string and looks for org and project info, setting the +// actual address to the base. Note that if a very malformed URL is passed in, +// this may not return what one expects. For now this is on purpose to avoid +// requiring error handling. // // This also removes any trailing "/v1"; we'll use that in our commands so we // don't require it from users. @@ -449,6 +450,14 @@ func (c *Client) SetAddr(addr string) error { return c.config.setAddr(addr) } +// Org fetches the org the client will use by default +func (c *Client) Org() string { + c.modifyLock.Lock() + defer c.modifyLock.Unlock() + + return c.config.Org +} + // SetOrg sets the org the client will use by default func (c *Client) SetOrg(org string) { c.modifyLock.Lock() @@ -457,6 +466,14 @@ func (c *Client) SetOrg(org string) { c.config.Org = org } +// Project fetches the project the client will use by default +func (c *Client) Project() string { + c.modifyLock.Lock() + defer c.modifyLock.Unlock() + + return c.config.Project +} + // SetProject sets the project the client will use by default func (c *Client) SetProject(project string) { c.modifyLock.Lock() @@ -519,6 +536,14 @@ func (c *Client) SetOutputCurlString(curl bool) { c.config.OutputCurlString = curl } +// Token gets the configured token. +func (c *Client) Token() string { + c.modifyLock.Lock() + defer c.modifyLock.Unlock() + + return c.config.Token +} + // SetToken sets the token directly. This won't perform any auth // verification, it simply sets the token properly for future requests. func (c *Client) SetToken(token string) { @@ -700,18 +725,25 @@ func (c *Client) NewRequest(ctx context.Context, method, requestPath string, bod orgProjPath = path.Join(orgProjPath, "projects", project) } + p := path.Join(u.Path, "/v1", orgProjPath) + if strings.HasPrefix(requestPath, ":") { + p = p + requestPath + } else { + p = path.Join(p, requestPath) + } + req := &http.Request{ Method: method, URL: &url.URL{ User: u.User, Scheme: u.Scheme, Host: host, - Path: path.Join(u.Path, "/v1", orgProjPath, requestPath), + Path: p, }, Host: u.Host, } req.Header = headers - req.Header.Add("authorization", "bearer: "+token) + req.Header.Add("authorization", "Bearer "+token) req.Header.Set("content-type", "application/json") if ctx != nil { req = req.WithContext(ctx) @@ -786,7 +818,7 @@ func (c *Client) Do(r *retryablehttp.Request) (*Response, error) { } result, err := client.Do(r) - if result != nil && err == nil && result.StatusCode == 307 { + if result != nil && err == nil && result.StatusCode == http.StatusTemporaryRedirect { loc, err := result.Location() if err != nil { return nil, fmt.Errorf("error getting new location during redirect: %w", err) diff --git a/api/error.gen.go b/api/error.gen.go index 094dd067f..744808ef3 100644 --- a/api/error.gen.go +++ b/api/error.gen.go @@ -3,6 +3,7 @@ package api import ( "encoding/json" + "strings" "github.com/fatih/structs" @@ -13,22 +14,36 @@ type Error struct { defaultFields []string // The HTTP Status code applicable to this error - Status *int32 `json:"status,omitempty"` + Status int32 `json:"status,omitempty"` // An application-specific error string - Code *string `json:"code,omitempty"` + Code string `json:"code,omitempty"` // A human readable explanation specific to this occurrence of the error - Message *string `json:"message,omitempty"` + Message string `json:"message,omitempty"` // Additional metadata regarding the error. Depending on the error // different fields will be populated. Details *ErrorDetails `json:"details,omitempty"` } func (s *Error) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"code": "code", "details": "details", "message": "message", "status": "status"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *Error) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"code": "code", "details": "details", "message": "message", "status": "status"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s Error) MarshalJSON() ([]byte, error) { diff --git a/api/error_details.gen.go b/api/error_details.gen.go index a1d43e438..523728c9f 100644 --- a/api/error_details.gen.go +++ b/api/error_details.gen.go @@ -3,6 +3,7 @@ package api import ( "encoding/json" + "strings" "github.com/fatih/structs" @@ -12,17 +13,31 @@ import ( type ErrorDetails struct { defaultFields []string - TraceId *string `json:"TraceId,omitempty"` - RequestId *string `json:"request_id,omitempty"` + TraceId string `json:"TraceId,omitempty"` + RequestId string `json:"request_id,omitempty"` RequestFields []*FieldError `json:"request_fields,omitempty"` } func (s *ErrorDetails) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"requestfields": "request_fields", "requestid": "request_id", "traceid": "TraceId"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *ErrorDetails) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"requestfields": "request_fields", "requestid": "request_id", "traceid": "TraceId"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s ErrorDetails) MarshalJSON() ([]byte, error) { diff --git a/api/field_error.gen.go b/api/field_error.gen.go index 8d42db7c9..9e0c8c13e 100644 --- a/api/field_error.gen.go +++ b/api/field_error.gen.go @@ -3,6 +3,7 @@ package api import ( "encoding/json" + "strings" "github.com/fatih/structs" @@ -12,16 +13,30 @@ import ( type FieldError struct { defaultFields []string - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` } func (s *FieldError) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"description": "description", "name": "name"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *FieldError) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"description": "description", "name": "name"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s FieldError) MarshalJSON() ([]byte, error) { diff --git a/api/groups/group.gen.go b/api/groups/group.gen.go index 7da80595b..85ddd4d6b 100644 --- a/api/groups/group.gen.go +++ b/api/groups/group.gen.go @@ -3,6 +3,7 @@ package groups import ( "encoding/json" + "strings" "time" "github.com/fatih/structs" @@ -34,11 +35,25 @@ type Group struct { } func (s *Group) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "disabled": "disabled", "id": "id", "name": "name", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *Group) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "disabled": "disabled", "id": "id", "name": "name", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s Group) MarshalJSON() ([]byte, error) { diff --git a/api/groups/group_test.go b/api/groups/group_test.go index 5eb6a3902..d19904539 100644 --- a/api/groups/group_test.go +++ b/api/groups/group_test.go @@ -116,8 +116,8 @@ func TestGroup_Crud(t *testing.T) { checkGroup := func(step string, g *groups.Group, apiErr *api.Error, err error, wantedName string) { assert := assert.New(t) assert.NoError(err, step) - if !assert.Nil(apiErr, step) && apiErr.Message != nil { - t.Errorf("ApiError message: %q", *apiErr.Message) + if !assert.Nil(apiErr, step) && apiErr.Message != "" { + t.Errorf("ApiError message: %q", apiErr.Message) } assert.NotNil(g, "returned no resource", step) gotName := "" @@ -216,17 +216,17 @@ func TestGroup_Errors(t *testing.T) { _, apiErr, err = tt.scope.ReadGroup(ctx, &groups.Group{Id: iam.GroupPrefix + "_doesntexis"}) assert.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusNotFound) + assert.EqualValues(apiErr.Status, http.StatusNotFound) _, apiErr, err = tt.scope.ReadGroup(ctx, &groups.Group{Id: "invalid id"}) assert.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusBadRequest) + assert.EqualValues(apiErr.Status, http.StatusBadRequest) _, apiErr, err = tt.scope.UpdateGroup(ctx, &groups.Group{Id: u.Id}) assert.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusBadRequest) + assert.EqualValues(apiErr.Status, http.StatusBadRequest) }) } } diff --git a/api/hosts/host.gen.go b/api/hosts/host.gen.go index b8380f145..3d1d62ec6 100644 --- a/api/hosts/host.gen.go +++ b/api/hosts/host.gen.go @@ -3,6 +3,7 @@ package hosts import ( "encoding/json" + "strings" "time" "github.com/fatih/structs" @@ -38,11 +39,25 @@ type Host struct { } func (s *Host) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"address": "address", "createdtime": "created_time", "description": "description", "disabled": "disabled", "id": "id", "name": "name", "type": "type", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *Host) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"address": "address", "createdtime": "created_time", "description": "description", "disabled": "disabled", "id": "id", "name": "name", "type": "type", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s Host) MarshalJSON() ([]byte, error) { diff --git a/api/hosts/host_catalog.gen.go b/api/hosts/host_catalog.gen.go index b9c99d186..fb9617c25 100644 --- a/api/hosts/host_catalog.gen.go +++ b/api/hosts/host_catalog.gen.go @@ -3,6 +3,7 @@ package hosts import ( "encoding/json" + "strings" "time" "github.com/fatih/structs" @@ -39,11 +40,25 @@ type HostCatalog struct { } func (s *HostCatalog) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"attributes": "attributes", "createdtime": "created_time", "description": "description", "disabled": "disabled", "id": "id", "name": "name", "type": "type", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *HostCatalog) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"attributes": "attributes", "createdtime": "created_time", "description": "description", "disabled": "disabled", "id": "id", "name": "name", "type": "type", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s HostCatalog) MarshalJSON() ([]byte, error) { diff --git a/api/hosts/host_catalog_test.go b/api/hosts/host_catalog_test.go index c185b0a23..d11f838ca 100644 --- a/api/hosts/host_catalog_test.go +++ b/api/hosts/host_catalog_test.go @@ -29,8 +29,8 @@ func TestCatalogs_Crud(t *testing.T) { checkCatalog := func(step string, hc *hosts.HostCatalog, apiErr *api.Error, err error, wantedName string) { assert := assert.New(t) assert.NoError(err, step) - if !assert.Nil(apiErr, step) && apiErr.Message != nil { - t.Errorf("ApiError message: %q", *apiErr.Message) + if !assert.Nil(apiErr, step) && apiErr.Message != "" { + t.Errorf("ApiError message: %q", apiErr.Message) } assert.NotNil(hc, "returned no resource", step) gotName := "" @@ -94,15 +94,15 @@ func TestCatalogs_Errors(t *testing.T) { assert.NoError(err) // TODO: Should this be nil instead of just a catalog that has no values set assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusNotFound) + assert.EqualValues(apiErr.Status, http.StatusNotFound) _, apiErr, err = p.ReadHostCatalog(ctx, &hosts.HostCatalog{Id: "invalid id"}) assert.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusBadRequest) + assert.EqualValues(apiErr.Status, http.StatusBadRequest) _, apiErr, err = p.UpdateHostCatalog(ctx, &hosts.HostCatalog{Id: hc.Id, Type: api.String("Cant Update")}) assert.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusBadRequest) + assert.EqualValues(apiErr.Status, http.StatusBadRequest) } diff --git a/api/hosts/host_set.gen.go b/api/hosts/host_set.gen.go index 46fbcff48..4af2fcf13 100644 --- a/api/hosts/host_set.gen.go +++ b/api/hosts/host_set.gen.go @@ -3,6 +3,7 @@ package hosts import ( "encoding/json" + "strings" "time" "github.com/fatih/structs" @@ -44,11 +45,25 @@ type HostSet struct { } func (s *HostSet) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "disabled": "disabled", "hosts": "hosts", "id": "id", "name": "name", "size": "size", "type": "type", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *HostSet) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "disabled": "disabled", "hosts": "hosts", "id": "id", "name": "name", "size": "size", "type": "type", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s HostSet) MarshalJSON() ([]byte, error) { diff --git a/api/internal/genapi/input.go b/api/internal/genapi/input.go index 38de4dc9e..f3dfbd49c 100644 --- a/api/internal/genapi/input.go +++ b/api/internal/genapi/input.go @@ -12,150 +12,119 @@ type structInfo struct { parentName string detailName string templateType templateType + nameJsonMap map[string]string } var inputStructs = []*structInfo{ { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go", - "Error", - os.Getenv("GEN_BASEPATH") + "/api/error.gen.go", - "Error", - "api", - "", - "", - "", - templateTypeResource, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go", + inName: "Error", + outFile: os.Getenv("GEN_BASEPATH") + "/api/error.gen.go", + outName: "Error", + outPkg: "api", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go", - "ErrorDetails", - os.Getenv("GEN_BASEPATH") + "/api/error_details.gen.go", - "ErrorDetails", - "api", - "", - "", - "", - templateTypeResource, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go", + inName: "ErrorDetails", + outFile: os.Getenv("GEN_BASEPATH") + "/api/error_details.gen.go", + outName: "ErrorDetails", + outPkg: "api", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go", - "FieldError", - os.Getenv("GEN_BASEPATH") + "/api/field_error.gen.go", - "FieldError", - "api", - "", - "", - "", - templateTypeResource, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go", + inName: "FieldError", + outFile: os.Getenv("GEN_BASEPATH") + "/api/field_error.gen.go", + outName: "FieldError", + outPkg: "api", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host.pb.go", - "Host", - os.Getenv("GEN_BASEPATH") + "/api/hosts/host.gen.go", - "Host", - "hosts", - "", - "", - "", - templateTypeResource, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host.pb.go", + inName: "Host", + outFile: os.Getenv("GEN_BASEPATH") + "/api/hosts/host.gen.go", + outName: "Host", + outPkg: "hosts", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_set.pb.go", - "HostSet", - os.Getenv("GEN_BASEPATH") + "/api/hosts/host_set.gen.go", - "HostSet", - "hosts", - "", - "", - "", - templateTypeResource, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_set.pb.go", + inName: "HostSet", + outFile: os.Getenv("GEN_BASEPATH") + "/api/hosts/host_set.gen.go", + outName: "HostSet", + outPkg: "hosts", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go", - "HostCatalog", - os.Getenv("GEN_BASEPATH") + "/api/hosts/host_catalog.gen.go", - "HostCatalog", - "hosts", - "", - "", - "", - templateTypeResource, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go", + inName: "HostCatalog", + outFile: os.Getenv("GEN_BASEPATH") + "/api/hosts/host_catalog.gen.go", + outName: "HostCatalog", + outPkg: "hosts", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/scopes/org.pb.go", - "Org", - os.Getenv("GEN_BASEPATH") + "/api/scopes/org.gen.go", - "Org", - "scopes", - "", - "", - "", - templateTypeResource, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/scopes/org.pb.go", + inName: "Org", + outFile: os.Getenv("GEN_BASEPATH") + "/api/scopes/org.gen.go", + outName: "Org", + outPkg: "scopes", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/scopes/project.pb.go", - "Project", - os.Getenv("GEN_BASEPATH") + "/api/scopes/project.gen.go", - "Project", - "scopes", - "", - "", - "", - templateTypeResource, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/authtokens/authtoken.pb.go", + inName: "AuthToken", + outFile: os.Getenv("GEN_BASEPATH") + "/api/authtokens/authtoken.gen.go", + outName: "AuthToken", + outPkg: "authtokens", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/users/user.pb.go", - "User", - os.Getenv("GEN_BASEPATH") + "/api/users/user.gen.go", - "User", - "users", - "", - "", - "", - templateTypeResource, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/scopes/project.pb.go", + inName: "Project", + outFile: os.Getenv("GEN_BASEPATH") + "/api/scopes/project.gen.go", + outName: "Project", + outPkg: "scopes", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/groups/group.pb.go", - "Group", - os.Getenv("GEN_BASEPATH") + "/api/groups/group.gen.go", - "Group", - "groups", - "", - "", - "", - templateTypeResource, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/users/user.pb.go", + inName: "User", + outFile: os.Getenv("GEN_BASEPATH") + "/api/users/user.gen.go", + outName: "User", + outPkg: "users", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/roles/role.pb.go", - "Role", - os.Getenv("GEN_BASEPATH") + "/api/roles/role.gen.go", - "Role", - "roles", - "", - "", - "", - templateTypeResource, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/groups/group.pb.go", + inName: "Group", + outFile: os.Getenv("GEN_BASEPATH") + "/api/groups/group.gen.go", + outName: "Group", + outPkg: "groups", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go", - "StaticHostCatalogDetails", - os.Getenv("GEN_BASEPATH") + "/api/hosts/static_host_catalog.gen.go", - "StaticHostCatalogDetails", - "hosts", - "", - "HostCatalog", - "StaticHostCatalog", - templateTypeDetail, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/roles/role.pb.go", + inName: "Role", + outFile: os.Getenv("GEN_BASEPATH") + "/api/roles/role.gen.go", + outName: "Role", + outPkg: "roles", }, { - os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go", - "AwsEc2HostCatalogDetails", - os.Getenv("GEN_BASEPATH") + "/api/hosts/awsec2_host_catalog.gen.go", - "AwsEc2HostCatalogDetails", - "hosts", - "", - "HostCatalog", - "AwsEc2HostCatalog", - templateTypeDetail, + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/roles/role.pb.go", + inName: "Principal", + outFile: os.Getenv("GEN_BASEPATH") + "/api/roles/principal.gen.go", + outName: "Principal", + outPkg: "roles", + }, + { + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go", + inName: "StaticHostCatalogDetails", + outFile: os.Getenv("GEN_BASEPATH") + "/api/hosts/static_host_catalog.gen.go", + outName: "StaticHostCatalogDetails", + outPkg: "hosts", + parentName: "HostCatalog", + detailName: "StaticHostCatalog", + templateType: templateTypeDetail, + }, + { + inFile: os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go", + inName: "AwsEc2HostCatalogDetails", + outFile: os.Getenv("GEN_BASEPATH") + "/api/hosts/awsec2_host_catalog.gen.go", + outName: "AwsEc2HostCatalogDetails", + outPkg: "hosts", + parentName: "HostCatalog", + detailName: "AwsEc2HostCatalog", + templateType: templateTypeDetail, }, } diff --git a/api/internal/genapi/parse.go b/api/internal/genapi/parse.go index 2db7fc5a3..99bc72eb6 100644 --- a/api/internal/genapi/parse.go +++ b/api/internal/genapi/parse.go @@ -38,6 +38,7 @@ func parsePBs() { os.Exit(1) } + inputStruct.nameJsonMap = make(map[string]string) ast.Walk(visitFn(func(n ast.Node) { spec, ok := n.(*ast.TypeSpec) if !ok { @@ -114,11 +115,6 @@ func parsePBs() { var selectorExpr *ast.SelectorExpr switch typ := field.Type.(type) { case *ast.Ident: - // Id values are immutable and should always exist, so - // don't make it a pointer - if field.Names[0].Name != "Id" { - typ.Name = "*" + typ.Name - } goto TAGMODIFY case *ast.ArrayType: goto TAGMODIFY @@ -222,7 +218,10 @@ func parsePBs() { } TAGMODIFY: - st.Fields.List[i].Tag.Value = "`" + regex.FindString(st.Fields.List[i].Tag.Value) + "`" + tagString := regex.FindString(st.Fields.List[i].Tag.Value) + st.Fields.List[i].Tag.Value = "`" + tagString + "`" + tagString = strings.TrimSuffix(strings.TrimPrefix(tagString, `json:"`), `,omitempty"`) + inputStruct.nameJsonMap[strings.ToLower(field.Names[0].Name)] = tagString } }), inAst) diff --git a/api/internal/genapi/read.go b/api/internal/genapi/read.go index 2de7aeb7f..38ef2ed73 100644 --- a/api/internal/genapi/read.go +++ b/api/internal/genapi/read.go @@ -37,11 +37,6 @@ var readFuncs = map[string][]*readInfo{ targetType: "users.User", path: "users", }, - { - baseType: "Project", - targetType: "hosts.HostCatalog", - path: "host-catalogs", - }, { baseType: "Project", targetType: "groups.Group", @@ -52,6 +47,11 @@ var readFuncs = map[string][]*readInfo{ targetType: "roles.Role", path: "roles", }, + { + baseType: "Project", + targetType: "hosts.HostCatalog", + path: "host-catalogs", + }, }, } diff --git a/api/internal/genapi/templates.go b/api/internal/genapi/templates.go index c3a8f4d30..bac51cfbd 100644 --- a/api/internal/genapi/templates.go +++ b/api/internal/genapi/templates.go @@ -25,10 +25,12 @@ func writeStructTemplates() { Name string Package string StructFields string + NameJsonMap map[string]string }{ Name: inputStruct.outName, Package: inputStruct.outPkg, StructFields: inputStruct.structFields, + NameJsonMap: inputStruct.nameJsonMap, }) case templateTypeDetail: @@ -61,11 +63,12 @@ func writeStructTemplates() { // TODO: Add documentation around SetDefault and how it behaves when the field corresponding to the provided key is already set. var structTemplate = template.Must(template.New("").Parse( `// Code generated by "make api"; DO NOT EDIT. -package {{ .Package }} +package {{ .Package }} import ( "context" "encoding/json" + "strings" "github.com/fatih/structs" @@ -82,11 +85,25 @@ type {{ .Name }} struct { } func (s *{{ .Name }}) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := {{ printf "%#v" .NameJsonMap }} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *{{ .Name }}) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := {{ printf "%#v" .NameJsonMap }} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s {{ .Name }}) MarshalJSON() ([]byte, error) { diff --git a/api/response.go b/api/response.go index 7c87ec141..eaf6a2dc9 100644 --- a/api/response.go +++ b/api/response.go @@ -32,25 +32,40 @@ func (r *Response) Decode(inStruct interface{}) (*Error, error) { return nil, nil } + // TODO Remove as we'll use the common err format for this + if r.resp.StatusCode == 403 { + // Nothing to be done + return &Error{ + Status: http.StatusForbidden, + Message: "Forbidden", + }, nil + } + if inStruct == nil { return nil, fmt.Errorf("nil value given to decode into and not a 204 response") } - r.Body = new(bytes.Buffer) - if _, err := r.Body.ReadFrom(r.resp.Body); err != nil { - return nil, fmt.Errorf("error reading response body: %w", err) + apiErr := &Error{ + Status: int32(r.resp.StatusCode), } + if r.resp.Body != nil { + r.Body = new(bytes.Buffer) + if _, err := r.Body.ReadFrom(r.resp.Body); err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } - dec := json.NewDecoder(bytes.NewReader(r.Body.Bytes())) - var apiErr Error - if r.resp.StatusCode >= 400 { - inStruct = &apiErr - } - if err := dec.Decode(inStruct); err != nil { - return nil, fmt.Errorf("error decoding response: %w; response was %s", err, r.Body.String()) + if r.Body.Len() != 0 { + dec := json.NewDecoder(bytes.NewReader(r.Body.Bytes())) + if r.resp.StatusCode >= 400 { + inStruct = apiErr + } + if err := dec.Decode(inStruct); err != nil { + return nil, fmt.Errorf("error decoding response: %w; response was %s", err, r.Body.String()) + } + } } if r.resp.StatusCode >= 400 { - return &apiErr, nil + return apiErr, nil } return nil, nil diff --git a/api/roles/custom.go b/api/roles/custom.go index 86152eda8..0f2895e20 100644 --- a/api/roles/custom.go +++ b/api/roles/custom.go @@ -7,16 +7,17 @@ import ( "github.com/hashicorp/watchtower/api" ) -func (s Role) AddPrincipals(ctx context.Context, groups, users []string) (*Role, *api.Error, error) { +func (s Role) AddPrincipals(ctx context.Context, principalIds []string) (*Role, *api.Error, error) { if s.Client == nil { return nil, nil, fmt.Errorf("nil client in AddPrincipals request") } // We assume that the client provided has the org and optionally the project id of the request. body := map[string]interface{}{ - "group_ids": groups, - "user_ids": users, - "version": s.Version, + "version": s.Version, + } + if len(principalIds) > 0 { + body["principal_ids"] = principalIds } req, err := s.Client.NewRequest(ctx, "POST", fmt.Sprintf("roles/%s:add-principals", s.Id), body) @@ -40,16 +41,20 @@ func (s Role) AddPrincipals(ctx context.Context, groups, users []string) (*Role, return target, apiErr, nil } -func (s Role) SetPrincipals(ctx context.Context, groups, users []string) (*Role, *api.Error, error) { +func (s Role) SetPrincipals(ctx context.Context, principalIds []string) (*Role, *api.Error, error) { if s.Client == nil { return nil, nil, fmt.Errorf("nil client in SetPrincipals request") } // We assume that the client provided has the org and optionally the project id of the request. body := map[string]interface{}{ - "group_ids": groups, - "user_ids": users, - "version": s.Version, + "version": s.Version, + } + if len(principalIds) > 0 { + body["principal_ids"] = principalIds + } else if principalIds != nil { + // In this function, a non-nil but empty list means clear out + body["principal_ids"] = nil } req, err := s.Client.NewRequest(ctx, "POST", fmt.Sprintf("roles/%s:set-principals", s.Id), body) @@ -73,16 +78,17 @@ func (s Role) SetPrincipals(ctx context.Context, groups, users []string) (*Role, return target, apiErr, nil } -func (s Role) RemovePrincipals(ctx context.Context, groups, users []string) (*Role, *api.Error, error) { +func (s Role) RemovePrincipals(ctx context.Context, principalIds []string) (*Role, *api.Error, error) { if s.Client == nil { return nil, nil, fmt.Errorf("nil client in RemovePrincipals request") } // We assume that the client provided has the org and optionally the project id of the request. body := map[string]interface{}{ - "group_ids": groups, - "user_ids": users, - "version": s.Version, + "version": s.Version, + } + if len(principalIds) > 0 { + body["principal_ids"] = principalIds } req, err := s.Client.NewRequest(ctx, "POST", fmt.Sprintf("roles/%s:remove-principals", s.Id), body) @@ -113,9 +119,11 @@ func (s Role) AddGrants(ctx context.Context, grants []string) (*Role, *api.Error // We assume that the client provided has the org and optionally the project id of the request. body := map[string]interface{}{ - "grants": grants, "version": s.Version, } + if len(grants) > 0 { + body["grants"] = grants + } req, err := s.Client.NewRequest(ctx, "POST", fmt.Sprintf("roles/%s:add-grants", s.Id), body) if err != nil { @@ -145,9 +153,13 @@ func (s Role) SetGrants(ctx context.Context, grants []string) (*Role, *api.Error // We assume that the client provided has the org and optionally the project id of the request. body := map[string]interface{}{ - "grants": grants, "version": s.Version, } + if len(grants) > 0 { + body["grants"] = grants + } else if grants != nil { + body["grants"] = nil + } req, err := s.Client.NewRequest(ctx, "POST", fmt.Sprintf("roles/%s:set-grants", s.Id), body) if err != nil { @@ -177,9 +189,11 @@ func (s Role) RemoveGrants(ctx context.Context, grants []string) (*Role, *api.Er // We assume that the client provided has the org and optionally the project id of the request. body := map[string]interface{}{ - "grants": grants, "version": s.Version, } + if len(grants) > 0 { + body["grants"] = grants + } req, err := s.Client.NewRequest(ctx, "POST", fmt.Sprintf("roles/%s:remove-grants", s.Id), body) if err != nil { diff --git a/api/roles/principal.gen.go b/api/roles/principal.gen.go new file mode 100644 index 000000000..eea447e2b --- /dev/null +++ b/api/roles/principal.gen.go @@ -0,0 +1,61 @@ +// Code generated by "make api"; DO NOT EDIT. +package roles + +import ( + "encoding/json" + "strings" + + "github.com/fatih/structs" + + "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/internal/strutil" +) + +type Principal struct { + Client *api.Client `json:"-"` + + defaultFields []string + + // The ID of the principal. + // Output only. + Id string `json:"id,omitempty"` + // The type of the principal. + // Output only. + Type string `json:"type,omitempty"` + // The scope ID of the principal. + // Output only. + ScopeId string `json:"scope_id,omitempty"` +} + +func (s *Principal) SetDefault(key string) { + lowerKey := strings.ToLower(key) + validMap := map[string]string{"id": "id", "scopeid": "scope_id", "type": "type"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } +} + +func (s *Principal) UnsetDefault(key string) { + lowerKey := strings.ToLower(key) + validMap := map[string]string{"id": "id", "scopeid": "scope_id", "type": "type"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } +} + +func (s Principal) MarshalJSON() ([]byte, error) { + m := structs.Map(s) + if m == nil { + m = make(map[string]interface{}) + } + for _, k := range s.defaultFields { + m[k] = nil + } + return json.Marshal(m) +} diff --git a/api/roles/role.gen.go b/api/roles/role.gen.go index 246c7a66d..3676574ff 100644 --- a/api/roles/role.gen.go +++ b/api/roles/role.gen.go @@ -3,6 +3,7 @@ package roles import ( "encoding/json" + "strings" "time" "github.com/fatih/structs" @@ -40,13 +41,14 @@ type Role struct { // The version can be used in subsiquent write requests to ensure this resource // has not changed and to fail the write if it has. // Output only. - Version *uint32 `json:"version,omitempty"` - // The principals that are assigned this role. + Version uint32 `json:"version,omitempty"` + // The IDs (only) of principals that are assigned to this role. // Output only. - UserIds []string `json:"user_ids,omitempty"` + PrincipalIds []string `json:"principal_ids,omitempty"` + // The principals that are assigned to this role. // Output only. - GroupIds []string `json:"group_ids,omitempty"` - // The grants that this role provides for it's principals. + Principals []*Principal `json:"principals,omitempty"` + // The grants that this role provides for its principals. // Output only. Grants []string `json:"grants,omitempty"` // The canonical version of the grants in the grants field with the same index. @@ -58,11 +60,25 @@ type Role struct { } func (s *Role) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "disabled": "disabled", "grants": "grants", "grantscanonical": "grants_canonical", "grantscopeid": "grant_scope_id", "grantsjson": "grants_json", "id": "id", "name": "name", "principalids": "principal_ids", "principals": "principals", "updatedtime": "updated_time", "version": "version"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *Role) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "disabled": "disabled", "grants": "grants", "grantscanonical": "grants_canonical", "grantscopeid": "grant_scope_id", "grantsjson": "grants_json", "id": "id", "name": "name", "principalids": "principal_ids", "principals": "principals", "updatedtime": "updated_time", "version": "version"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s Role) MarshalJSON() ([]byte, error) { diff --git a/api/roles/role_test.go b/api/roles/role_test.go index c430f3e57..77455bda5 100644 --- a/api/roles/role_test.go +++ b/api/roles/role_test.go @@ -6,6 +6,7 @@ import ( "net/http" "testing" + "github.com/hashicorp/vault/sdk/helper/strutil" "github.com/hashicorp/watchtower/api" "github.com/hashicorp/watchtower/api/groups" "github.com/hashicorp/watchtower/api/roles" @@ -27,7 +28,7 @@ type roleCrud interface { } func TestCustom(t *testing.T) { - tc := controller.NewTestController(t, nil) + tc := controller.NewTestController(t, &controller.TestControllerOpts{DisableAuthorizationFailures: true}) defer tc.Shutdown() client := tc.Client() @@ -56,6 +57,22 @@ func TestCustom(t *testing.T) { }, } + hasPrincipal := func(role *roles.Role, principalId string) bool { + var foundInPrincipals bool + var foundInPrincipalIds bool + for _, v := range role.Principals { + if v.Id == principalId { + foundInPrincipals = true + } + } + for _, v := range role.PrincipalIds { + if v == principalId { + foundInPrincipalIds = true + } + } + return foundInPrincipals && foundInPrincipalIds + } + for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { require := require.New(t) @@ -70,63 +87,52 @@ func TestCustom(t *testing.T) { require.Nil(apiErr) require.NotNil(r) - updatedRole, apiErr, err := r.AddPrincipals(ctx, []string{g.Id}, nil) + updatedRole, apiErr, err := r.AddPrincipals(ctx, []string{g.Id}) require.NoError(err) - require.Nil(apiErr, "Got error ", errorMessage(apiErr)) - assert.Equal(t, *updatedRole.Version, (*r.Version)+1) - assert.Contains(t, updatedRole.GroupIds, g.Id) - assert.Empty(t, updatedRole.UserIds) + require.Nil(apiErr, "Got error ", apiErr) + assert.Equal(t, updatedRole.Version, r.Version+1) + assert.True(t, hasPrincipal(updatedRole, g.Id)) r = updatedRole - updatedRole, apiErr, err = r.SetPrincipals(ctx, nil, []string{user.Id}) + updatedRole, apiErr, err = r.SetPrincipals(ctx, []string{user.Id}) require.NoError(err) - require.Nil(apiErr, "Got error ", errorMessage(apiErr)) - assert.Equal(t, *updatedRole.Version, (*r.Version)+1) - assert.Empty(t, updatedRole.GroupIds) - assert.Contains(t, updatedRole.UserIds, user.Id) + require.Nil(apiErr, "Got error ", apiErr) + assert.Equal(t, updatedRole.Version, r.Version+1) + assert.True(t, hasPrincipal(updatedRole, user.Id)) r = updatedRole - updatedRole, apiErr, err = r.RemovePrincipals(ctx, nil, []string{user.Id}) + updatedRole, apiErr, err = r.RemovePrincipals(ctx, []string{user.Id}) require.NoError(err) - require.Nil(apiErr, "Got error ", errorMessage(apiErr)) - assert.Equal(t, *updatedRole.Version, (*r.Version)+1) - assert.Empty(t, updatedRole.GroupIds) - assert.Empty(t, updatedRole.UserIds) + require.Nil(apiErr, "Got error ", apiErr) + assert.Equal(t, updatedRole.Version, r.Version+1) + assert.Empty(t, updatedRole.Principals) r = updatedRole updatedRole, apiErr, err = r.AddGrants(ctx, []string{"id=*;actions=read"}) require.NoError(err) - require.Nil(apiErr, "Got error ", errorMessage(apiErr)) - assert.Equal(t, *updatedRole.Version, (*r.Version)+1) + require.Nil(apiErr, "Got error ", apiErr) + assert.Equal(t, updatedRole.Version, r.Version+1) assert.Contains(t, updatedRole.Grants, "id=*;actions=read") r = updatedRole updatedRole, apiErr, err = r.SetGrants(ctx, []string{"id=*;actions=*"}) require.NoError(err) - require.Nil(apiErr, "Got error ", errorMessage(apiErr)) - assert.Equal(t, *updatedRole.Version, (*r.Version)+1) + require.Nil(apiErr, "Got error ", apiErr) + assert.Equal(t, updatedRole.Version, r.Version+1) assert.Contains(t, updatedRole.Grants, "id=*;actions=*") r = updatedRole updatedRole, apiErr, err = r.RemoveGrants(ctx, []string{"id=*;actions=*"}) require.NoError(err) - require.Nil(apiErr, "Got error ", errorMessage(apiErr)) - assert.Equal(t, *updatedRole.Version, (*r.Version)+1) + require.Nil(apiErr, "Got error ", apiErr) + assert.Equal(t, updatedRole.Version, r.Version+1) assert.Empty(t, updatedRole.Grants) }) } } -func errorMessage(in *api.Error) string { - if in == nil { - return "" - } - return *in.Message -} - func TestRole_List(t *testing.T) { - assert := assert.New(t) - tc := controller.NewTestController(t, nil) + tc := controller.NewTestController(t, &controller.TestControllerOpts{DisableAuthorizationFailures: true}) defer tc.Shutdown() client := tc.Client() @@ -153,12 +159,19 @@ func TestRole_List(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) ctx := context.Background() - pl, apiErr, err := tc.scope.ListRoles(ctx) - assert.NoError(err) + p1, apiErr, err := tc.scope.ListRoles(ctx) + require.NoError(err) assert.Nil(apiErr) - assert.Empty(pl) + var defaultRoleIds []string + if tc.name == "org" { + require.True(2 == len(p1)) + defaultRoleIds = []string{p1[0].Id, p1[1].Id} + } else { + require.Empty(p1) + } var expected []*roles.Role for i := 0; i < 10; i++ { @@ -166,21 +179,35 @@ func TestRole_List(t *testing.T) { } expected[0], apiErr, err = tc.scope.CreateRole(ctx, expected[0]) - assert.NoError(err) + require.NoError(err) assert.Nil(apiErr) - pl, apiErr, err = tc.scope.ListRoles(ctx) + p2, apiErr, err := tc.scope.ListRoles(ctx) assert.NoError(err) assert.Nil(apiErr) - assert.ElementsMatch(comparableSlice(expected[:1]), comparableSlice(pl)) + var p2ForComparison []*roles.Role + for _, v := range p2 { + if !strutil.StrListContains(defaultRoleIds, v.Id) { + p2ForComparison = append(p2ForComparison, v) + } + } + assert.ElementsMatch(comparableSlice(expected[:1]), comparableSlice(p2ForComparison)) for i := 1; i < 10; i++ { expected[i], apiErr, err = tc.scope.CreateRole(ctx, expected[i]) assert.NoError(err) assert.Nil(apiErr) } - pl, apiErr, err = tc.scope.ListRoles(ctx) - assert.ElementsMatch(comparableSlice(expected), comparableSlice(pl)) + p3, apiErr, err := tc.scope.ListRoles(ctx) + require.NoError(err) + assert.Nil(apiErr) + var p3ForComparison []*roles.Role + for _, v := range p3 { + if !strutil.StrListContains(defaultRoleIds, v.Id) { + p3ForComparison = append(p3ForComparison, v) + } + } + assert.ElementsMatch(comparableSlice(expected), comparableSlice(p3ForComparison)) }) } } @@ -202,7 +229,7 @@ func comparableSlice(in []*roles.Role) []roles.Role { } func TestRole_Crud(t *testing.T) { - tc := controller.NewTestController(t, nil) + tc := controller.NewTestController(t, &controller.TestControllerOpts{DisableAuthorizationFailures: true}) defer tc.Shutdown() client := tc.Client() @@ -217,8 +244,8 @@ func TestRole_Crud(t *testing.T) { checkRole := func(step string, g *roles.Role, apiErr *api.Error, err error, wantedName string) { assert := assert.New(t) assert.NoError(err, step) - if !assert.Nil(apiErr, step) && apiErr.Message != nil { - t.Errorf("ApiError message: %q", *apiErr.Message) + if !assert.Nil(apiErr, step) && apiErr.Message != "" { + t.Errorf("ApiError message: %q", apiErr.Message) } assert.NotNil(g, "returned no resource", step) gotName := "" @@ -244,6 +271,7 @@ func TestRole_Crud(t *testing.T) { for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) g, apiErr, err := tt.scope.CreateRole(tc.Context(), &roles.Role{Name: api.String("foo")}) checkRole("create", g, apiErr, err, "foo") @@ -261,19 +289,20 @@ func TestRole_Crud(t *testing.T) { checkRole("update", g, apiErr, err, "") existed, apiErr, err := tt.scope.DeleteRole(tc.Context(), g) - assert.NoError(t, err) - assert.True(t, existed, "Expected existing user when deleted, but it wasn't.") + require.NoError(err) + assert.Nil(apiErr) + assert.True(existed, "Expected existing user when deleted, but it wasn't.") existed, apiErr, err = tt.scope.DeleteRole(tc.Context(), g) - assert.NoError(t, err) - assert.False(t, existed, "Expected user to not exist when deleted, but it did.") + require.NoError(err) + assert.Nil(apiErr) + assert.False(existed, "Expected user to not exist when deleted, but it did.") }) } } func TestRole_Errors(t *testing.T) { - assert := assert.New(t) - tc := controller.NewTestController(t, nil) + tc := controller.NewTestController(t, &controller.TestControllerOpts{DisableAuthorizationFailures: true}) defer tc.Shutdown() ctx := tc.Context() @@ -302,30 +331,31 @@ func TestRole_Errors(t *testing.T) { for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) u, apiErr, err := tt.scope.CreateRole(ctx, &roles.Role{Name: api.String("first")}) - assert.NoError(err) + require.NoError(err) assert.Nil(apiErr) assert.NotNil(u) // Create another resource with the same name. _, apiErr, err = tt.scope.CreateRole(ctx, &roles.Role{Name: api.String("first")}) - assert.NoError(err) + require.NoError(err) assert.NotNil(apiErr) _, apiErr, err = tt.scope.ReadRole(ctx, &roles.Role{Id: iam.RolePrefix + "_doesntexis"}) - assert.NoError(err) + require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusNotFound) + assert.EqualValues(http.StatusNotFound, apiErr.Status) _, apiErr, err = tt.scope.ReadRole(ctx, &roles.Role{Id: "invalid id"}) - assert.NoError(err) + require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusBadRequest) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) _, apiErr, err = tt.scope.UpdateRole(ctx, &roles.Role{Id: u.Id}) - assert.NoError(err) + require.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusBadRequest) + assert.EqualValues(http.StatusBadRequest, apiErr.Status) }) } } diff --git a/api/scopes/authenticate.go b/api/scopes/authenticate.go new file mode 100644 index 000000000..d1a2e9306 --- /dev/null +++ b/api/scopes/authenticate.go @@ -0,0 +1,54 @@ +package scopes + +import ( + "context" + "fmt" + + "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/authtokens" +) + +// TODO: This will need to be changed when we add Auth Method API to watchtower. We'll also need a better +// way to handle different auth method types. +func (s Org) Authenticate(ctx context.Context, authMethodId, name, password string) (*authtokens.AuthToken, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in Authenticate request") + } + if s.Id == "" { + // Assume the client has been configured with org already and + // move on + } else { + // If it's explicitly set here, override anything that might be in the + // client + ctx = context.WithValue(ctx, "org", s.Id) + } + + reqBody := map[string]interface{}{ + "credentials": map[string]string{ + "name": name, + "password": password, + }, + } + + req, err := s.Client.NewRequest(ctx, "POST", fmt.Sprintf("auth-methods/%s:authenticate", authMethodId), reqBody) + if err != nil { + return nil, nil, fmt.Errorf("error creating Authenticate request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during Authenticate call: %w", err) + } + + target := new(authtokens.AuthToken) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding Authenticate response: %w", err) + } + + if target.Token != "" { + s.Client.SetToken(target.Token) + } + + return target, apiErr, nil +} diff --git a/api/scopes/authenticate_test.go b/api/scopes/authenticate_test.go new file mode 100644 index 000000000..e6f879672 --- /dev/null +++ b/api/scopes/authenticate_test.go @@ -0,0 +1,34 @@ +package scopes_test + +import ( + "context" + "net/http" + "testing" + + "github.com/hashicorp/watchtower/api/scopes" + "github.com/hashicorp/watchtower/internal/servers/controller" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAuthenticate(t *testing.T) { + assert := assert.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + org := &scopes.Org{ + Client: client, + } + ctx := context.Background() + + tok, apiErr, err := org.Authenticate(ctx, "am_anything", "admin", "hunter2") + assert.NoError(err) + assert.Nil(apiErr) + assert.NotNil(tok) + + _, apiErr, err = org.Authenticate(ctx, "am_anything", "wrong", "wrong") + assert.NoError(err) + require.NotNil(t, apiErr) + assert.EqualValuesf(http.StatusUnauthorized, apiErr.Status, "Expected unauthenticated, got %q", apiErr.Message) +} diff --git a/api/scopes/org.gen.go b/api/scopes/org.gen.go index 112441220..af6c57313 100644 --- a/api/scopes/org.gen.go +++ b/api/scopes/org.gen.go @@ -3,6 +3,7 @@ package scopes import ( "encoding/json" + "strings" "time" "github.com/fatih/structs" @@ -32,11 +33,25 @@ type Org struct { } func (s *Org) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "id": "id", "name": "name", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *Org) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "id": "id", "name": "name", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s Org) MarshalJSON() ([]byte, error) { diff --git a/api/scopes/project.gen.go b/api/scopes/project.gen.go index a3e5559bc..5c030c77a 100644 --- a/api/scopes/project.gen.go +++ b/api/scopes/project.gen.go @@ -3,6 +3,7 @@ package scopes import ( "encoding/json" + "strings" "time" "github.com/fatih/structs" @@ -34,11 +35,25 @@ type Project struct { } func (s *Project) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "disabled": "disabled", "id": "id", "name": "name", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *Project) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "disabled": "disabled", "id": "id", "name": "name", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s Project) MarshalJSON() ([]byte, error) { diff --git a/api/scopes/project_test.go b/api/scopes/project_test.go index 953fbb32d..fbcb41992 100644 --- a/api/scopes/project_test.go +++ b/api/scopes/project_test.go @@ -133,10 +133,10 @@ func TestProject_Errors(t *testing.T) { assert.NoError(err) // TODO: Should this be nil instead of just a Project that has no values set assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusNotFound) + assert.EqualValues(apiErr.Status, http.StatusNotFound) _, apiErr, err = org.ReadProject(ctx, &scopes.Project{Id: "invalid id"}) assert.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusBadRequest) + assert.EqualValues(apiErr.Status, http.StatusBadRequest) } diff --git a/api/scopes/read.gen.go b/api/scopes/read.gen.go index 9b940801b..acdbde2d1 100644 --- a/api/scopes/read.gen.go +++ b/api/scopes/read.gen.go @@ -177,47 +177,6 @@ func (s Org) ReadUser(ctx context.Context, r *users.User) (*users.User, *api.Err return target, apiErr, nil } -func (s Project) ReadHostCatalog(ctx context.Context, r *hosts.HostCatalog) (*hosts.HostCatalog, *api.Error, error) { - if s.Client == nil { - return nil, nil, fmt.Errorf("nil client in ReadHostCatalog request") - } - if s.Id == "" { - - // Assume the client has been configured with project already and move - // on - - } else { - // If it's explicitly set here, override anything that might be in the - // client - - ctx = context.WithValue(ctx, "project", s.Id) - - } - if r.Id == "" { - return nil, nil, fmt.Errorf("empty hosts.HostCatalog ID field in ReadHostCatalog request") - } - - req, err := s.Client.NewRequest(ctx, "GET", fmt.Sprintf("%s/%s", "host-catalogs", r.Id), r) - if err != nil { - return nil, nil, fmt.Errorf("error creating ReadHostCatalog request: %w", err) - } - - resp, err := s.Client.Do(req) - if err != nil { - return nil, nil, fmt.Errorf("error performing client request during ReadHostCatalog call: %w", err) - } - - target := new(hosts.HostCatalog) - apiErr, err := resp.Decode(target) - if err != nil { - return nil, nil, fmt.Errorf("error decoding ReadHostCatalog repsonse: %w", err) - } - - target.Client = s.Client - - return target, apiErr, nil -} - func (s Project) ReadGroup(ctx context.Context, r *groups.Group) (*groups.Group, *api.Error, error) { if s.Client == nil { return nil, nil, fmt.Errorf("nil client in ReadGroup request") @@ -299,3 +258,44 @@ func (s Project) ReadRole(ctx context.Context, r *roles.Role) (*roles.Role, *api return target, apiErr, nil } + +func (s Project) ReadHostCatalog(ctx context.Context, r *hosts.HostCatalog) (*hosts.HostCatalog, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in ReadHostCatalog request") + } + if s.Id == "" { + + // Assume the client has been configured with project already and move + // on + + } else { + // If it's explicitly set here, override anything that might be in the + // client + + ctx = context.WithValue(ctx, "project", s.Id) + + } + if r.Id == "" { + return nil, nil, fmt.Errorf("empty hosts.HostCatalog ID field in ReadHostCatalog request") + } + + req, err := s.Client.NewRequest(ctx, "GET", fmt.Sprintf("%s/%s", "host-catalogs", r.Id), r) + if err != nil { + return nil, nil, fmt.Errorf("error creating ReadHostCatalog request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during ReadHostCatalog call: %w", err) + } + + target := new(hosts.HostCatalog) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding ReadHostCatalog repsonse: %w", err) + } + + target.Client = s.Client + + return target, apiErr, nil +} diff --git a/api/users/user.gen.go b/api/users/user.gen.go index eb036001e..a11bfb664 100644 --- a/api/users/user.gen.go +++ b/api/users/user.gen.go @@ -3,6 +3,7 @@ package users import ( "encoding/json" + "strings" "time" "github.com/fatih/structs" @@ -34,11 +35,25 @@ type User struct { } func (s *User) SetDefault(key string) { - s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "disabled": "disabled", "id": "id", "name": "name", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, v) + return + } + } } func (s *User) UnsetDefault(key string) { - s.defaultFields = strutil.StrListDelete(s.defaultFields, key) + lowerKey := strings.ToLower(key) + validMap := map[string]string{"createdtime": "created_time", "description": "description", "disabled": "disabled", "id": "id", "name": "name", "updatedtime": "updated_time"} + for k, v := range validMap { + if k == lowerKey || v == lowerKey { + s.defaultFields = strutil.StrListDelete(s.defaultFields, v) + return + } + } } func (s User) MarshalJSON() ([]byte, error) { diff --git a/api/users/user_test.go b/api/users/user_test.go index 21a780711..0837ac36a 100644 --- a/api/users/user_test.go +++ b/api/users/user_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/watchtower/internal/iam" "github.com/hashicorp/watchtower/internal/servers/controller" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestUsers_List(t *testing.T) { @@ -50,6 +51,8 @@ func TestUsers_List(t *testing.T) { assert.Nil(apiErr) } ul, apiErr, err = org.ListUsers(ctx) + require.NoError(t, err) + assert.Nil(apiErr) assert.ElementsMatch(comparableSlice(expected), comparableSlice(ul)) } @@ -81,8 +84,8 @@ func TestUser_Crud(t *testing.T) { checkUser := func(step string, u *users.User, apiErr *api.Error, err error, wantedName string) { assert := assert.New(t) assert.NoError(err, step) - if !assert.Nil(apiErr, step) && apiErr.Message != nil { - t.Errorf("ApiError message: %q", *apiErr.Message) + if !assert.Nil(apiErr, step) && apiErr.Message != "" { + t.Errorf("ApiError message: %q", apiErr.Message) } assert.NotNil(u, "returned no resource", step) gotName := "" @@ -108,12 +111,14 @@ func TestUser_Crud(t *testing.T) { u, apiErr, err = org.UpdateUser(tc.Context(), u) checkUser("update", u, apiErr, err, "") - existed, apiErr, err := org.DeleteUser(tc.Context(), u) - assert.NoError(t, err) + existed, _, err := org.DeleteUser(tc.Context(), u) + require.NoError(t, err) + assert.Nil(t, apiErr) assert.True(t, existed, "Expected existing user when deleted, but it wasn't.") existed, apiErr, err = org.DeleteUser(tc.Context(), u) - assert.NoError(t, err) + require.NoError(t, err) + assert.Nil(t, apiErr) assert.False(t, existed, "Expected user to not exist when deleted, but it did.") } @@ -141,15 +146,15 @@ func TestUser_Errors(t *testing.T) { _, apiErr, err = org.ReadUser(ctx, &users.User{Id: iam.UserPrefix + "_doesntexis"}) assert.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusNotFound) + assert.EqualValues(apiErr.Status, http.StatusNotFound) _, apiErr, err = org.ReadUser(ctx, &users.User{Id: "invalid id"}) assert.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusBadRequest) + assert.EqualValues(apiErr.Status, http.StatusBadRequest) _, apiErr, err = org.UpdateUser(ctx, &users.User{Id: u.Id}) assert.NoError(err) assert.NotNil(apiErr) - assert.EqualValues(*apiErr.Status, http.StatusBadRequest) + assert.EqualValues(apiErr.Status, http.StatusBadRequest) } diff --git a/globals/globals.go b/globals/globals.go index fbfa9ac38..fa2ad8e25 100644 --- a/globals/globals.go +++ b/globals/globals.go @@ -9,6 +9,10 @@ import "time" // import issues. These values should only ever be set at startup, but simply // available to reference from anywhere. +const ( + ContextUserIdValue = "wt_user_id" +) + var ( // DefaultMaxRequestDuration is the amount of time we'll wait for a request DefaultMaxRequestDuration = 90 * time.Second diff --git a/go.mod b/go.mod index 3d883a9ad..39f19a635 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/go-openapi/swag v0.19.9 // indirect github.com/go-openapi/validate v0.19.8 // indirect github.com/go-swagger/go-swagger v0.23.0 + github.com/go-test/deep v1.0.3 // indirect github.com/golang-migrate/migrate/v4 v4.11.0 github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe github.com/golang/protobuf v1.4.2 @@ -29,6 +30,7 @@ require ( github.com/hashicorp/go-hclog v0.14.1 github.com/hashicorp/go-kms-wrapping v0.5.10 github.com/hashicorp/go-multierror v1.1.0 + github.com/hashicorp/go-plugin v1.2.0 // indirect github.com/hashicorp/go-retryablehttp v0.6.6 github.com/hashicorp/go-rootcerts v1.0.2 github.com/hashicorp/go-sockaddr v1.0.2 @@ -44,6 +46,7 @@ require ( github.com/mattn/go-colorable v0.1.6 github.com/mitchellh/cli v1.1.1 github.com/mitchellh/go-homedir v1.1.0 + github.com/mitchellh/go-wordwrap v1.0.0 github.com/mitchellh/gox v1.0.1 github.com/mitchellh/mapstructure v1.3.2 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect @@ -57,7 +60,9 @@ require ( github.com/ryanuber/go-glob v1.0.0 github.com/spf13/viper v1.6.3 // indirect github.com/stretchr/testify v1.6.1 + github.com/zalando/go-keyring v0.1.0 go.mongodb.org/mongo-driver v1.3.2 // indirect + golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 golang.org/x/net v0.0.0-20200519113804-d87ec0cfa476 // indirect golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f // indirect golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 diff --git a/go.sum b/go.sum index 200c97f2d..b7a090c77 100644 --- a/go.sum +++ b/go.sum @@ -248,6 +248,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= +github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU= +github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -430,6 +432,8 @@ github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3a github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= @@ -469,6 +473,8 @@ github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY9 github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/gocql/gocql v0.0.0-20190402132108-0e1d5de854df h1:fwXmhM0OqixzJDOGgTSyNH9eEDij9uGTXwsyWXvyR0A= github.com/gocql/gocql v0.0.0-20190402132108-0e1d5de854df/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= +github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4= +github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -639,6 +645,8 @@ github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+ github.com/hashicorp/go-plugin v1.0.0/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cRWgnE= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-plugin v1.2.0 h1:CUfYokW0EJNDcGecVrHZK//Cp1GFlHwoqtcUIEiU6BY= +github.com/hashicorp/go-plugin v1.2.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0= github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a h1:FmnBDwGwlTgugDGbVxwV8UavqSMACbGrUpfc98yFLR4= github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a/go.mod h1:xbXnmKqX9/+RhPkJ4zrEx4738HacP72aaUPlT2RZ4sU= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= @@ -837,6 +845,7 @@ github.com/jefferai/jsonx v1.0.0 h1:Xoz0ZbmkpBvED5W9W1B5B/zc3Oiq7oXqiW7iRV3B6EI= github.com/jefferai/jsonx v1.0.0/go.mod h1:OGmqmi2tTeI/PS+qQfBDToLHHJIy/RMp24fPo8vFvoQ= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= github.com/jhump/protoreflect v1.6.2-0.20200602154950-9a9113f80b57 h1:DDbkVWVTpzxHbD80e2zpbczB+gkTWI1w2gSeL+viRhc= github.com/jhump/protoreflect v1.6.2-0.20200602154950-9a9113f80b57/go.mod h1:RZkzh7Hi9J7qT/sPlWnJ/UwZqCJvciFxKDA0UCeltSM= github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= @@ -982,6 +991,7 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI= @@ -1256,6 +1266,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zalando/go-keyring v0.1.0 h1:ffq972Aoa4iHNzBlUHgK5Y+k8+r/8GvcGd80/OFZb/k= +github.com/zalando/go-keyring v0.1.0/go.mod h1:RaxNwUITJaHVdQ0VC7pELPZ3tOWn13nr0gZMZEhpVU0= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1367,6 +1379,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180801234040-f4c29de78a2a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/internal/cmd/base/base.go b/internal/cmd/base/base.go index f90ca8013..4c997e0fa 100644 --- a/internal/cmd/base/base.go +++ b/internal/cmd/base/base.go @@ -18,6 +18,7 @@ import ( "github.com/mitchellh/cli" "github.com/pkg/errors" "github.com/posener/complete" + "github.com/zalando/go-keyring" ) const ( @@ -26,6 +27,8 @@ const ( // NotSetValue is a flag value for a not-set value NotSetValue = "(not set)" + + envTokenName = "WATCHTOWER_TOKEN_NAME" ) // reRemoveWhitespace is a regular expression for stripping whitespace from @@ -53,6 +56,7 @@ type Command struct { flagFormat string flagField string + FlagTokenName string flagOutputCurlString bool client *api.Client @@ -161,6 +165,26 @@ func (c *Command) Client() (*api.Client, error) { c.client.SetMaxRetries(0) } + if c.client.Token() == "" { + tokenName := "default" + if c.FlagTokenName != "" { + tokenName = c.FlagTokenName + } + if tokenName != "none" { + token, err := keyring.Get("HashiCorp Watchtower Auth Token", tokenName) + if err != nil { + if err == keyring.ErrNotFound { + c.UI.Info("No saved credential found, continuing without") + } else { + c.UI.Error(fmt.Sprintf("Error reading auth token from system credential store: %s", err)) + } + } + if token != "" { + c.client.SetToken(token) + } + } + } + return c.client, nil } @@ -169,6 +193,7 @@ type FlagSetBit uint const ( FlagSetNone FlagSetBit = 1 << iota FlagSetHTTP + FlagSetClient FlagSetOutputField FlagSetOutputFormat ) @@ -274,6 +299,17 @@ func (c *Command) FlagSet(bit FlagSetBit) *FlagSets { "is highly discouraged as it decreases the security of data " + "transmissions to and from the Watchtower server.", }) + } + + if bit&FlagSetClient != 0 { + f := set.NewFlagSet("Client Options") + + f.StringVar(&StringVar{ + Name: "token-name", + Target: &c.FlagTokenName, + EnvVar: envTokenName, + Usage: "If specified, the given value will be used as the name when storing the token in the system credential store. This can allow switching user identities for different commands.", + }) f.BoolVar(&BoolVar{ Name: "output-curl-string", diff --git a/internal/cmd/base/format.go b/internal/cmd/base/format.go index a7511b42b..f39e80a08 100644 --- a/internal/cmd/base/format.go +++ b/internal/cmd/base/format.go @@ -3,11 +3,15 @@ package base import ( "encoding/json" "fmt" + "os" "sort" "strings" + "unicode" + "unicode/utf8" "github.com/ghodss/yaml" "github.com/mitchellh/cli" + "github.com/mitchellh/go-wordwrap" "github.com/ryanuber/columnize" ) @@ -17,6 +21,39 @@ const ( hopeDelim = "♨" ) +// This is adapted from the code in the strings package for TrimSpace +var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1} + +func trimSpaceRight(in string) string { + for stop := len(in); stop > 0; stop-- { + c := in[stop-1] + if c >= utf8.RuneSelf { + return strings.TrimFunc(in[:stop], unicode.IsSpace) + } + if asciiSpace[c] == 0 { + return in[0:stop] + } + } + return "" +} + +func WrapForHelpText(lines []string) string { + var ret []string + for _, line := range lines { + line = trimSpaceRight(line) + trimmed := strings.TrimSpace(line) + diff := uint(len(line) - len(trimmed)) + wrapped := wordwrap.WrapString(trimmed, TermWidth-diff) + splitWrapped := strings.Split(wrapped, "\n") + for i := range splitWrapped { + splitWrapped[i] = fmt.Sprintf("%s%s", strings.Repeat(" ", int(diff)), strings.TrimSpace(splitWrapped[i])) + } + ret = append(ret, strings.Join(splitWrapped, "\n")) + } + + return strings.Join(ret, "\n") +} + type FormatOptions struct { Format string } @@ -25,7 +62,7 @@ type FormatOptions struct { type JsonFormatter struct{} func (j JsonFormatter) Format(data interface{}) ([]byte, error) { - return json.MarshalIndent(data, "", " ") + return json.Marshal(data) } /* @@ -346,3 +383,29 @@ func looksLikeDuration(k string) bool { k == "duration" || strings.HasSuffix(k, "_duration") || k == "lease_max" || k == "ttl_max" } + +type Formatter interface { + //Output(ui cli.Ui, secret *api.Secret, data interface{}) error + Format(data interface{}) ([]byte, error) +} + +var Formatters = map[string]Formatter{ + "json": JsonFormatter{}, + "table": TableFormatter{}, + "yaml": YamlFormatter{}, + "yml": YamlFormatter{}, +} + +func Format(ui cli.Ui) string { + switch ui.(type) { + case *WatchtowerUI: + return ui.(*WatchtowerUI).Format + } + + format := os.Getenv(EnvWatchtowerCLIFormat) + if format == "" { + format = "table" + } + + return format +} diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go index 998c490c4..f01f51823 100644 --- a/internal/cmd/base/servers.go +++ b/internal/cmd/base/servers.go @@ -27,6 +27,7 @@ import ( "github.com/hashicorp/watchtower/globals" "github.com/hashicorp/watchtower/internal/db" "github.com/hashicorp/watchtower/internal/iam" + "github.com/hashicorp/watchtower/internal/servers/controller/handlers/authenticate" "github.com/hashicorp/watchtower/version" "github.com/jinzhu/gorm" "github.com/mitchellh/cli" @@ -59,7 +60,8 @@ type Server struct { Listeners []*ServerListener - DefaultOrgId string + DefaultOrgId string + DevAuthMethodId string DevDatabaseUrl string DevDatabaseCleanupFunc func() error @@ -418,40 +420,86 @@ func (b *Server) CreateDevDatabase(dialect string) error { cancel() }() - var scope *iam.Scope + var orgScope *iam.Scope if b.DefaultOrgId != "" { - scope, err = repo.LookupScope(ctx, b.DefaultOrgId) + orgScope, err = repo.LookupScope(ctx, b.DefaultOrgId) if err != nil { c() return fmt.Errorf("error looking up existing scope with org ID %q: %w", b.DefaultOrgId, err) } - if scope != nil { - goto INFO - } } - scope, err = iam.NewOrg() - if err != nil { - c() - return fmt.Errorf("error creating new org scope: %w", err) - } - scope, err = repo.CreateScope(ctx, scope, iam.WithPublicId(b.DefaultOrgId)) - if err != nil { - c() - return fmt.Errorf("error persisting new org scope: %w", err) - } - if b.DefaultOrgId != "" { - if scope.GetPublicId() != b.DefaultOrgId { + if orgScope == nil { + orgScope, err = iam.NewOrg() + if err != nil { c() - return fmt.Errorf("expected org ID %q, got %q after persisting", b.DefaultOrgId, scope.GetPublicId()) + return fmt.Errorf("error creating new org scope: %w", err) + } + orgScope, err = repo.CreateScope(ctx, orgScope, iam.WithPublicId(b.DefaultOrgId)) + if err != nil { + c() + return fmt.Errorf("error persisting new org scope: %w", err) + } + if b.DefaultOrgId != "" { + if orgScope.GetPublicId() != b.DefaultOrgId { + c() + return fmt.Errorf("expected org ID %q, got %q after persisting", b.DefaultOrgId, orgScope.GetPublicId()) + } + } else { + b.DefaultOrgId = orgScope.GetPublicId() } - } else { - b.DefaultOrgId = scope.GetPublicId() } -INFO: - b.InfoKeys = append(b.InfoKeys, "dev org id") + ar, err := iam.NewRole(orgScope.PublicId) + if err != nil { + return fmt.Errorf("error creating in memory role for anon authen: %w", err) + } + authenRole, err := repo.CreateRole(ctx, ar, iam.WithDescription("role for anon authen")) + if err != nil { + return fmt.Errorf("error creating role for anon authen: %w", err) + } + if _, err := repo.AddRoleGrants(ctx, authenRole.PublicId, authenRole.Version, []string{"type=auth-method;actions=list,authenticate"}); err != nil { + return fmt.Errorf("error creating grant for anon authen: %w", err) + } + if _, err := repo.AddPrincipalRoles(ctx, authenRole.PublicId, authenRole.Version+1, []string{"u_anon"}, nil); err != nil { + return fmt.Errorf("error adding principal to role for anon authen: %w", err) + } + + pr, err := iam.NewRole(orgScope.PublicId) + if err != nil { + return fmt.Errorf("error creating in memory role for default dev grants: %w", err) + } + defPermsRole, err := repo.CreateRole(ctx, pr, iam.WithDescription("role for def grants")) + if err != nil { + return fmt.Errorf("error creating role for default dev grants: %w", err) + } + if _, err := repo.AddRoleGrants(ctx, defPermsRole.PublicId, defPermsRole.Version, []string{"id=*;actions=*"}); err != nil { + return fmt.Errorf("error creating grant for default dev grants: %w", err) + } + if _, err := repo.AddPrincipalRoles(ctx, defPermsRole.PublicId, defPermsRole.Version+1, []string{"u_auth"}, nil); err != nil { + return fmt.Errorf("error adding principal to role for default dev grants: %w", err) + } + + // TODO: Remove this when Auth Account repo is in place. + authenticate.OrgScope = orgScope.GetPublicId() + insert := `insert into auth_method + (public_id, scope_id) + values + ($1, $2);` + amId := b.DevAuthMethodId + if amId == "" { + amId = "am_1234567890" + } + authenticate.RWDb.Store(rw) + _, err = b.Database.DB().Exec(insert, amId, orgScope.GetPublicId()) + if err != nil { + c() + return err + } + + b.InfoKeys = append(b.InfoKeys, "dev org id", "dev auth method id") b.Info["dev org id"] = b.DefaultOrgId + b.Info["dev auth method id"] = amId return nil } diff --git a/internal/cmd/base/ui.go b/internal/cmd/base/ui.go new file mode 100644 index 000000000..88f438587 --- /dev/null +++ b/internal/cmd/base/ui.go @@ -0,0 +1,22 @@ +package base + +import ( + "os" + + "github.com/mitchellh/cli" + "golang.org/x/crypto/ssh/terminal" +) + +type WatchtowerUI struct { + cli.Ui + Format string +} + +var TermWidth uint = 80 + +func init() { + width, _, err := terminal.GetSize(int(os.Stdin.Fd())) + if err == nil { + TermWidth = uint(width) + } +} diff --git a/internal/cmd/commands.go b/internal/cmd/commands.go index 81fb8bbc3..32ba724a7 100644 --- a/internal/cmd/commands.go +++ b/internal/cmd/commands.go @@ -6,10 +6,12 @@ import ( "syscall" "github.com/hashicorp/watchtower/internal/cmd/base" + "github.com/hashicorp/watchtower/internal/cmd/commands/authenticate" "github.com/hashicorp/watchtower/internal/cmd/commands/config" "github.com/hashicorp/watchtower/internal/cmd/commands/controller" "github.com/hashicorp/watchtower/internal/cmd/commands/dev" "github.com/hashicorp/watchtower/internal/cmd/commands/hosts" + "github.com/hashicorp/watchtower/internal/cmd/commands/roles" "github.com/hashicorp/watchtower/internal/cmd/commands/scopes" "github.com/hashicorp/watchtower/internal/cmd/commands/worker" @@ -82,6 +84,87 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Command: base.NewCommand(ui), }, nil }, + "authenticate": func() (cli.Command, error) { + return &authenticate.Command{ + Command: base.NewCommand(ui), + }, nil + }, + "authenticate password": func() (cli.Command, error) { + return &authenticate.PasswordCommand{ + Command: base.NewCommand(ui), + }, nil + }, + "roles": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + }, nil + }, + "roles create": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + Func: "create", + }, nil + }, + "roles update": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + Func: "update", + }, nil + }, + "roles read": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + Func: "read", + }, nil + }, + "roles delete": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + Func: "delete", + }, nil + }, + "roles list": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + Func: "list", + }, nil + }, + "roles add-principals": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + Func: "add-principals", + }, nil + }, + "roles set-principals": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + Func: "set-principals", + }, nil + }, + "roles remove-principals": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + Func: "remove-principals", + }, nil + }, + "roles add-grants": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + Func: "add-grants", + }, nil + }, + "roles set-grants": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + Func: "set-grants", + }, nil + }, + "roles remove-grants": func() (cli.Command, error) { + return &roles.Command{ + Command: base.NewCommand(ui), + Func: "remove-grants", + }, nil + }, } } diff --git a/internal/cmd/commands/authenticate/authenticate.go b/internal/cmd/commands/authenticate/authenticate.go new file mode 100644 index 000000000..7092c0b65 --- /dev/null +++ b/internal/cmd/commands/authenticate/authenticate.go @@ -0,0 +1,35 @@ +package authenticate + +import ( + "github.com/hashicorp/watchtower/internal/cmd/base" + "github.com/mitchellh/cli" + "github.com/mitchellh/go-wordwrap" +) + +var _ cli.Command = (*Command)(nil) + +type Command struct { + *base.Command +} + +func (c *Command) Synopsis() string { + return wordwrap.WrapString("Authenticate the Watchtower commandline client", base.TermWidth) +} + +func (c *Command) Help() string { + return base.WrapForHelpText([]string{ + "Usage: watchtower authenticate [sub command] [options] [args]", + "", + " This command authenticates the Watchtower commandline client using a specified auth method. Examples:", + "", + " Authenticate with password auth method:", + "", + " $ watchtower authenticate password -name foo -password bar", + "", + " Please see the auth method subcommand help for detailed usage information.", + }) +} + +func (c *Command) Run(args []string) int { + return cli.RunResultHelp +} diff --git a/internal/cmd/commands/authenticate/password.go b/internal/cmd/commands/authenticate/password.go new file mode 100644 index 000000000..fc439f6fb --- /dev/null +++ b/internal/cmd/commands/authenticate/password.go @@ -0,0 +1,163 @@ +package authenticate + +import ( + "fmt" + "os" + "strings" + "time" + + "github.com/hashicorp/vault/sdk/helper/password" + "github.com/hashicorp/watchtower/api/scopes" + "github.com/hashicorp/watchtower/internal/cmd/base" + "github.com/kr/pretty" + "github.com/mitchellh/cli" + "github.com/mitchellh/go-wordwrap" + "github.com/posener/complete" + "github.com/zalando/go-keyring" +) + +var _ cli.Command = (*PasswordCommand)(nil) +var _ cli.CommandAutocomplete = (*PasswordCommand)(nil) + +var envPassword = "WATCHTOWER_AUTHENTICATE_PASSWORD" +var envName = "WATCHTOWER_AUTHENTICATE_NAME" +var envMethodId = "WATCHTOWER_AUTHENTICATE_METHOD_ID" + +type PasswordCommand struct { + *base.Command + + flagName string + flagPassword string + flagMethodId string +} + +func (c *PasswordCommand) Synopsis() string { + return wordwrap.WrapString("Invoke the password auth method to authenticate with Watchtower", base.TermWidth) +} + +func (c *PasswordCommand) Help() string { + return base.WrapForHelpText([]string{ + "Usage: watchtower authenticate password [options] [args]", + "", + " Invoke the password auth method to authenticate the Watchtower CLI:", + "", + " $ watchtower authenticate password -name=foo -password=bar", + "", + " If more than one instance of the password auth method exists, use the -method-id flag:", + "", + ` $ watchtower authenticate password -method-id am_12345 -name foo -password "bar"`, + }) + c.Flags().Help() +} + +func (c *PasswordCommand) Flags() *base.FlagSets { + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + + f := set.NewFlagSet("Command Options") + + f.StringVar(&base.StringVar{ + Name: "name", + Target: &c.flagName, + EnvVar: envName, + Usage: "Login name", + }) + + f.StringVar(&base.StringVar{ + Name: "password", + Target: &c.flagPassword, + EnvVar: envPassword, + Usage: "Password", + }) + + f.StringVar(&base.StringVar{ + Name: "method-id", + Target: &c.flagMethodId, + EnvVar: envMethodId, + Usage: "Specify if more than one instance of a password auth method exists in the given org", + }) + + return set +} + +func (c *PasswordCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictAnything +} + +func (c *PasswordCommand) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *PasswordCommand) Run(args []string) int { + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + switch { + case c.flagName == "": + c.UI.Error("Name must be provided via -name") + return 1 + case c.flagMethodId == "": + c.UI.Error("Auth method ID must be provided via -method-id") + return 1 + } + + if c.flagPassword == "" { + fmt.Print("Password is not set as flag or in env, please enter it now (will be hidden): ") + value, err := password.Read(os.Stdin) + fmt.Print("\n") + if err != nil { + c.UI.Error(fmt.Sprintf("An error occurred attempting to read the password. The raw error message is shown below but usually this is because you attempted to pipe a value into the command or you are executing outside of a terminal (TTY). The raw error was:\n\n%s", err.Error())) + return 2 + } + c.flagPassword = strings.TrimSpace(value) + } + + client, err := c.Client() + if err != nil { + c.UI.Error(fmt.Sprintf("Error creating API client: %s", err.Error())) + return 2 + } + + org := &scopes.Org{ + Client: client, + } + + // note: Authenticate() calls SetToken() under the hood to set the + // auth bearer on the client so we do not need to do anything with the + // returned token after this call, so we ignore it + result, apiErr, err := org.Authenticate(c.Context, c.flagMethodId, c.flagName, c.flagPassword) + if err != nil { + c.UI.Error(fmt.Sprintf("Error trying to perform authentication: %s", err.Error())) + return 2 + } + if apiErr != nil { + c.UI.Error(fmt.Sprintf("Error from controller when performing authentication: %s", pretty.Sprint(apiErr))) + return 1 + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(base.WrapForHelpText([]string{ + "", + "Authentication information:", + fmt.Sprintf(" Token: %s", result.Token), + fmt.Sprintf(" User ID: %s", result.UserId), + fmt.Sprintf(" Expiration Time: %s", result.ExpirationTime.Local().Format(time.RFC3339)), + })) + } + + tokenName := "default" + if c.Command.FlagTokenName != "" { + tokenName = c.Command.FlagTokenName + } + if tokenName != "none" { + if err := keyring.Set("HashiCorp Watchtower Auth Token", tokenName, result.Token); err != nil { + c.UI.Error(fmt.Sprintf("Error saving auth token to system credential store: %s", err)) + return 1 + } + } + + return 0 +} diff --git a/internal/cmd/commands/controller/controller.go b/internal/cmd/commands/controller/controller.go index 795e08def..fe67b1dde 100644 --- a/internal/cmd/commands/controller/controller.go +++ b/internal/cmd/commands/controller/controller.go @@ -48,6 +48,7 @@ type Command struct { flagDevControllerAPIListenAddr string flagDevControllerClusterListenAddr string flagDevOrgId string + flagDevAuthMethodId string flagDevPassthroughDirectory string } @@ -119,6 +120,14 @@ func (c *Command) Flags() *base.FlagSets { "mode.", }) + f.StringVar(&base.StringVar{ + Name: "dev-auth-method-id", + Target: &c.flagDevAuthMethodId, + EnvVar: "WATCHTWER_DEV_AUTH_METHOD_ID", + Usage: "Auto-created auth method ID. This only applies when running in \"dev\" " + + "mode.", + }) + f.StringVar(&base.StringVar{ Name: "dev-admin-password", Target: &c.flagDevAdminPassword, @@ -336,6 +345,18 @@ func (c *Command) ParseFlagsAndConfig(args []string) int { if c.flagDevOrgId != "" { c.Config.DefaultOrgId = c.flagDevOrgId } + if c.flagDevAuthMethodId != "" { + if !strings.HasPrefix(c.flagDevAuthMethodId, "am_") { + c.UI.Error(fmt.Sprintf("Invalid dev auth method ID, must start with %q", "am_")) + return 1 + } + if len(c.flagDevAuthMethodId) != 13 { + c.UI.Error(fmt.Sprintf("Invalid dev auth method ID, must be 10 base62 characters after %q", "am_")) + return 1 + } + c.DevAuthMethodId = c.flagDevAuthMethodId + } + c.Config.PassthroughDirectory = c.flagDevPassthroughDirectory for _, l := range c.Config.Listeners { diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 5a5207656..19a5e4ad8 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -36,6 +36,7 @@ type Command struct { flagDev bool flagDevAdminPassword string flagDevOrgId string + flagDevAuthMethodId string flagDevControllerAPIListenAddr string flagDevControllerClusterListenAddr string flagDevPassthroughDirectory string @@ -92,6 +93,14 @@ func (c *Command) Flags() *base.FlagSets { "mode.", }) + f.StringVar(&base.StringVar{ + Name: "dev-auth-method-id", + Target: &c.flagDevAuthMethodId, + EnvVar: "WATCHTWER_DEV_AUTH_METHOD_ID", + Usage: "Auto-created auth method ID. This only applies when running in \"dev\" " + + "mode.", + }) + f.StringVar(&base.StringVar{ Name: "dev-admin-password", Target: &c.flagDevAdminPassword, @@ -155,6 +164,18 @@ func (c *Command) Run(args []string) int { if c.flagDevOrgId != "" { devConfig.DefaultOrgId = c.flagDevOrgId } + if c.flagDevAuthMethodId != "" { + if !strings.HasPrefix(c.flagDevAuthMethodId, "am_") { + c.UI.Error(fmt.Sprintf("Invalid dev auth method ID, must start with %q", "am_")) + return 1 + } + if len(c.flagDevAuthMethodId) != 13 { + c.UI.Error(fmt.Sprintf("Invalid dev auth method ID, must be 10 base62 characters after %q", "am_")) + return 1 + } + c.DevAuthMethodId = c.flagDevAuthMethodId + } + devConfig.PassthroughDirectory = c.flagDevPassthroughDirectory for _, l := range devConfig.Listeners { diff --git a/internal/cmd/commands/hosts/host_create.go b/internal/cmd/commands/hosts/host_create.go index 25742f425..aab95e7ad 100644 --- a/internal/cmd/commands/hosts/host_create.go +++ b/internal/cmd/commands/hosts/host_create.go @@ -45,7 +45,7 @@ Usage: watchtower hosts create } func (c *CreateCommand) Flags() *base.FlagSets { - set := c.FlagSet(base.FlagSetHTTP | base.FlagSetOutputFormat) + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) f := set.NewFlagSet("Command Options") diff --git a/internal/cmd/commands/roles/funcs.go b/internal/cmd/commands/roles/funcs.go new file mode 100644 index 000000000..11696053a --- /dev/null +++ b/internal/cmd/commands/roles/funcs.go @@ -0,0 +1,232 @@ +package roles + +import ( + "fmt" + "net/textproto" + "strings" + "time" + + "github.com/hashicorp/watchtower/api/roles" + "github.com/hashicorp/watchtower/internal/cmd/base" + "github.com/mitchellh/go-wordwrap" +) + +func synopsisFunc(inFunc string) string { + if inFunc == "" { + return wordwrap.WrapString("Manage Watchtower roles", base.TermWidth) + } + return wordwrap.WrapString(fmt.Sprintf("%s a role within Watchtower", textproto.CanonicalMIMEHeaderKey(inFunc)), base.TermWidth) +} + +func principalsGrantsSynopsisFunc(inFunc string, principals bool) string { + var in string + switchStr := "principals (users, groups)" + if !principals { + switchStr = "grants" + } + switch { + case strings.HasPrefix(inFunc, "add"): + in = fmt.Sprintf("Add %s to", switchStr) + case strings.HasPrefix(inFunc, "set"): + in = fmt.Sprintf("Set the full contents of the %s on", switchStr) + case strings.HasPrefix(inFunc, "remove"): + in = fmt.Sprintf("Remove %s from", switchStr) + } + return wordwrap.WrapString(fmt.Sprintf("%s a role within Watchtower", in), base.TermWidth) +} + +func baseHelp() string { + return base.WrapForHelpText([]string{ + "Usage: watchtower roles [sub command] [options] [args]", + "", + " This command allows operations on Watchtower roles. Examples:", + "", + " Create a role:", + "", + ` $ watchtower roles create -name foo -description "For ProdOps usage"`, + "", + " Add a grant to a role:", + "", + ` $ watchtower roles add-grants -id r_1234567890 -grant "type=host-catalog;actions=create,delete"`, + "", + " Please see the role subcommand help for detailed usage information.", + }) +} + +func createHelp() string { + return base.WrapForHelpText([]string{ + "Usage: watchtower roles create [options] [args]", + "", + " Create a role. Example:", + "", + ` $ watchtower roles create -name ops -description "Role for ops grants"`, + "", + "", + }) +} + +func updateHelp() string { + return base.WrapForHelpText([]string{ + "Usage: watchtower roles update [options] [args]", + "", + " Update a role given its ID. Example:", + "", + ` $ watchtower roles update -id r_1234567890 -description "Development host grants"`, + }) +} + +func readHelp() string { + return base.WrapForHelpText([]string{ + "Usage: watchtower roles read [options] [args]", + "", + " Read a role given its ID. Example:", + "", + ` $ watchtower roles read -id r_1234567890`, + }) +} + +func deleteHelp() string { + return base.WrapForHelpText([]string{ + "Usage: watchtower roles delete [options] [args]", + "", + " Delete a role given its ID. Example:", + "", + ` $ watchtower roles delete -id r_1234567890`, + }) +} + +func listHelp() string { + return base.WrapForHelpText([]string{ + "Usage: watchtower roles list [options] [args]", + "", + " List roles within a scope. Example:", + "", + ` $ watchtower roles list -org o_1234567890`, + }) +} + +func addPrincipalsHelp() string { + return base.WrapForHelpText([]string{ + "Usage: watchtower roles add-principals [options] [args]", + "", + ` Adds principals (users, groups) to a role given its ID. The "user" and "group" flags can be specified multiple times. Example:`, + "", + ` $ watchtower roles add-principals -id r_1234567890 -user u_1234567890`, + }) +} + +func setPrincipalsHelp() string { + return base.WrapForHelpText([]string{ + "Usage: watchtower roles set-principals [options] [args]", + "", + ` Sets the complete set of principals (users, groups) on a role given its ID. The "user" and "group" flags can be specified multiple times. Example:`, + "", + ` $ watchtower roles set-principals -id r_1234567890 -user u_anon -group sg_1234567890`, + }) +} + +func removePrincipalsHelp() string { + return base.WrapForHelpText([]string{ + "Usage: watchtower roles remove-principals [options] [args]", + "", + ` Removes principals (users, groups) from a role given its ID. The "user" and "group" flags can be specified multiple times. Example:`, + "", + ` $ watchtower roles remove-principals -id r_1234567890 -group sg_1234567890`, + }) +} + +func populateFlags(c *Command, f *base.FlagSet, flagNames []string) { + for _, name := range flagNames { + switch name { + case "id": + f.StringVar(&base.StringVar{ + Name: "id", + Target: &c.flagId, + Usage: "ID of the role to operate on", + }) + case "name": + f.StringVar(&base.StringVar{ + Name: "name", + Target: &c.flagName, + Usage: "Name to set on the role", + }) + case "description": + f.StringVar(&base.StringVar{ + Name: "description", + Target: &c.flagDescription, + Usage: "Description to set on the role", + }) + case "grantscopeid": + f.StringVar(&base.StringVar{ + Name: "grant-scope-id", + Target: &c.flagGrantScopeId, + Usage: "The scope ID for grants set on the role", + }) + case "principal": + f.StringSliceVar(&base.StringSliceVar{ + Name: "principal", + Target: &c.flagPrincipals, + Usage: "The principals (users or groups) to add, remove, or set. May be specified multiple times.", + }) + case "grant": + f.StringSliceVar(&base.StringSliceVar{ + Name: "grant", + Target: &c.flagGrants, + Usage: "The grants to add, remove, or set. May be specified multiple times. Can be in compact string format or JSON (be sure to escape JSON properly).", + }) + } + } +} + +func generateRoleTableOutput(role *roles.Role) string { + var output []string + if true { + output = []string{ + "", + "Role information:", + fmt.Sprintf(" ID: %s", role.Id), + fmt.Sprintf(" Created At: %s", role.CreatedTime.Local().Format(time.RFC3339)), + fmt.Sprintf(" Updated At: %s", role.UpdatedTime.Local().Format(time.RFC3339)), + fmt.Sprintf(" Version: %d", role.Version), + } + } + if role.Name != nil { + output = append(output, + fmt.Sprintf(" Name: %s", *role.Name), + ) + } + if role.Description != nil { + output = append(output, + fmt.Sprintf(" Description: %s", *role.Description), + ) + } + if role.GrantScopeId != nil { + output = append(output, + fmt.Sprintf(" Grant Scope ID: %s", *role.GrantScopeId), + ) + } + if len(role.Principals) > 0 { + output = append(output, + fmt.Sprintf(" Principals: %s", ""), + ) + } + for _, principal := range role.Principals { + output = append(output, + fmt.Sprintf(" ID: %s", principal.Id), + fmt.Sprintf(" Type: %s", principal.Type), + fmt.Sprintf(" Scope ID: %s", principal.ScopeId), + ) + } + if len(role.GrantsCanonical) > 0 { + output = append(output, + fmt.Sprintf(" Canonical Grants: %s", ""), + ) + } + for _, grant := range role.GrantsCanonical { + output = append(output, + fmt.Sprintf(" %s", grant), + ) + + } + return base.WrapForHelpText(output) +} diff --git a/internal/cmd/commands/roles/role.go b/internal/cmd/commands/roles/role.go new file mode 100644 index 000000000..d00994df0 --- /dev/null +++ b/internal/cmd/commands/roles/role.go @@ -0,0 +1,363 @@ +package roles + +import ( + "context" + "fmt" + + "github.com/hashicorp/vault/sdk/helper/strutil" + "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/roles" + "github.com/hashicorp/watchtower/api/scopes" + "github.com/hashicorp/watchtower/internal/cmd/base" + "github.com/hashicorp/watchtower/internal/perms" + "github.com/kr/pretty" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*Command)(nil) +var _ cli.CommandAutocomplete = (*Command)(nil) + +type Command struct { + *base.Command + + Func string + + flagId string + flagName string + flagDescription string + flagGrantScopeId string + flagPrincipals []string + flagGrants []string +} + +func (c *Command) Synopsis() string { + switch c.Func { + case "create", "update", "read", "delete", "list": + return synopsisFunc(c.Func) + case "add-principals", "set-principals", "remove-principals": + return principalsGrantsSynopsisFunc(c.Func, true) + case "add-grants", "set-grants", "remove-grants": + return principalsGrantsSynopsisFunc(c.Func, false) + } + return "Manage Watchtower roles" +} + +var helpMap = map[string]func() string{ + "create": createHelp, + "update": updateHelp, + "read": readHelp, + "delete": deleteHelp, + "list": listHelp, + "add-principals": addPrincipalsHelp, + "set-principals": setPrincipalsHelp, + "remove-principals": removePrincipalsHelp, + "add-grants": addPrincipalsHelp, + "set-grants": setPrincipalsHelp, + "remove-grants": removePrincipalsHelp, +} + +var flagsMap = map[string][]string{ + "create": {"name", "description", "grantscopeid"}, + "update": {"id", "name", "description", "grantscopeid"}, + "read": {"id"}, + "delete": {"id"}, + "add-principals": {"id", "principal"}, + "set-principals": {"id", "principal"}, + "remove-principals": {"id", "principal"}, + "add-grants": {"id", "grant"}, + "set-grants": {"id", "grant"}, + "remove-grants": {"id", "grant"}, +} + +func (c *Command) Help() string { + if c.Func == "" { + return baseHelp() + } + return helpMap[c.Func]() + "\n\n" + c.Flags().Help() +} + +func (c *Command) Flags() *base.FlagSets { + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + + f := set.NewFlagSet("Command Options") + + populateFlags(c, f, flagsMap[c.Func]) + return set +} + +func (c *Command) AutocompleteArgs() complete.Predictor { + return complete.PredictAnything +} + +func (c *Command) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *Command) Run(args []string) int { + if c.Func == "" { + return cli.RunResultHelp + } + + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + if strutil.StrListContains(flagsMap[c.Func], "id") && c.flagId == "" { + c.UI.Error("ID is required but not passed in via -id") + return 1 + } + + client, err := c.Client() + if err != nil { + c.UI.Error(fmt.Sprintf("Error creating API client: %s", err.Error())) + return 2 + } + + role := &roles.Role{ + Client: client, + Id: c.flagId, + } + switch c.flagName { + case "": + case "null": + role.SetDefault("name") + default: + role.Name = api.String(c.flagName) + } + switch c.flagDescription { + case "": + case "null": + role.SetDefault("description") + default: + role.Description = api.String(c.flagDescription) + } + switch c.flagGrantScopeId { + case "": + case "null": + role.SetDefault("grantscopeid") + default: + role.GrantScopeId = api.String(c.flagGrantScopeId) + } + + principals := c.flagPrincipals + grants := c.flagGrants + switch c.Func { + case "add-principals", "remove-principals": + if len(c.flagPrincipals) == 0 { + c.UI.Error("No principals supplied via -principal") + return 1 + } + + case "add-grants", "remove-grants": + if len(c.flagGrants) == 0 { + c.UI.Error("No grants supplied via -grant") + return 1 + } + + case "set-principals": + switch len(c.flagPrincipals) { + case 0: + case 1: + if c.flagPrincipals[0] == "null" { + principals = []string{} + } + } + if principals == nil { + c.UI.Error("No principals supplied via -principals") + return 1 + } + + case "set-grants": + switch len(c.flagGrants) { + case 0: + case 1: + if c.flagGrants[0] == "null" { + grants = []string{} + } + } + if grants == nil { + c.UI.Error("No grants supplied via -grant") + return 1 + } + } + + if len(grants) > 0 { + for _, grant := range grants { + _, err := perms.Parse("global", "", grant) + if err != nil { + c.UI.Error(fmt.Errorf("Grant %q could not be parsed successfully: %w", grant, err).Error()) + return 1 + } + } + } + + var apiErr *api.Error + + type crudl interface { + CreateRole(context.Context, *roles.Role) (*roles.Role, *api.Error, error) + UpdateRole(context.Context, *roles.Role) (*roles.Role, *api.Error, error) + ReadRole(context.Context, *roles.Role) (*roles.Role, *api.Error, error) + DeleteRole(context.Context, *roles.Role) (bool, *api.Error, error) + ListRoles(context.Context) ([]*roles.Role, *api.Error, error) + } + var actor crudl + var existed bool + var listedRoles []*roles.Role + + switch { + case client.Project() != "": + project := &scopes.Project{ + Client: client, + } + actor = project + + case client.Org() != "": + org := &scopes.Org{ + Client: client, + } + actor = org + + default: + // TODO: Handle global case + c.UI.Error("TODO") + } + + if actor == nil { + c.UI.Error("Unable to determine the right scope for the command") + return 1 + } + + // Perform check-and-set when needed + switch c.Func { + case "create", "read", "delete", "list": + // These don't udpate so don't need the existing version + default: + existingRole, existingApiErr, existingErr := actor.ReadRole(c.Context, role) + if existingErr != nil { + c.UI.Error(fmt.Sprintf("Error performing initial check-and-set read: %s", err.Error())) + return 2 + } + if existingApiErr != nil { + c.UI.Error(fmt.Sprintf("Error from controller when performing initial check-and-set read: %s", pretty.Sprint(apiErr))) + return 1 + } + role.Version = existingRole.Version + } + + switch c.Func { + case "create": + role, apiErr, err = actor.CreateRole(c.Context, role) + case "update": + role, apiErr, err = actor.UpdateRole(c.Context, role) + case "read": + role, apiErr, err = actor.ReadRole(c.Context, role) + case "delete": + existed, apiErr, err = actor.DeleteRole(c.Context, role) + case "list": + listedRoles, apiErr, err = actor.ListRoles(c.Context) + case "add-principals": + role, apiErr, err = role.AddPrincipals(c.Context, principals) + case "set-principals": + role, apiErr, err = role.SetPrincipals(c.Context, principals) + case "remove-principals": + role, apiErr, err = role.RemovePrincipals(c.Context, principals) + case "add-grants": + role, apiErr, err = role.AddGrants(c.Context, grants) + case "set-grants": + role, apiErr, err = role.SetGrants(c.Context, grants) + case "remove-grants": + role, apiErr, err = role.RemoveGrants(c.Context, grants) + } + + plural := "role" + if c.Func == "list" { + plural = "roles" + } + if err != nil { + c.UI.Error(fmt.Sprintf("Error trying to %s %s: %s", c.Func, plural, err.Error())) + return 2 + } + if apiErr != nil { + c.UI.Error(fmt.Sprintf("Error from controller when performing %s on %s: %s", c.Func, plural, pretty.Sprint(apiErr))) + return 1 + } + + switch c.Func { + case "delete": + switch base.Format(c.UI) { + case "json": + c.UI.Output("null") + case "table": + output := "The delete operation completed successfully" + switch existed { + case true: + output += "." + default: + output += ", however the resource did not exist at the time." + } + c.UI.Output(output) + } + return 0 + + case "list": + if len(listedRoles) == 0 { + c.UI.Output("No roles found") + return 0 + } + switch base.Format(c.UI) { + case "json": + b, err := base.JsonFormatter{}.Format(listedRoles) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting to JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + case "table": + var output []string + output = []string{ + "", + "Role information:", + } + for i, r := range listedRoles { + if i > 0 { + output = append(output, "") + } + if true { + output = append(output, + fmt.Sprintf(" ID: %s", r.Id), + ) + } + if r.Name != nil { + output = append(output, + fmt.Sprintf(" Name: %s", *r.Name), + ) + } + if r.Description != nil { + output = append(output, + fmt.Sprintf(" Description: %s", *r.Description), + ) + } + } + c.UI.Output(base.WrapForHelpText(output)) + } + return 0 + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateRoleTableOutput(role)) + case "json": + b, err := base.JsonFormatter{}.Format(role) + if err != nil { + c.UI.Error(fmt.Errorf("Error formatting to JSON: %w", err).Error()) + return 1 + } + c.UI.Output(string(b)) + } + + return 0 +} diff --git a/internal/cmd/commands/scopes/funcs.go b/internal/cmd/commands/scopes/funcs.go new file mode 100644 index 000000000..9f6eae690 --- /dev/null +++ b/internal/cmd/commands/scopes/funcs.go @@ -0,0 +1,36 @@ +package scopes + +import ( + "fmt" + "time" + + "github.com/hashicorp/watchtower/api/scopes" + "github.com/hashicorp/watchtower/internal/cmd/base" +) + +func printProject(in *scopes.Project) string { + var ret []string + // This if true is here to line up columns for easy editing + if true { + ret = append(ret, []string{ + "", + "Project information:", + fmt.Sprintf(" ID: %s", in.Id), + fmt.Sprintf(" Created Time: %s", in.CreatedTime.Local().Format(time.RFC3339)), + fmt.Sprintf(" Updated Time: %s", in.UpdatedTime.Local().Format(time.RFC3339)), + }..., + ) + } + if in.Name != nil { + ret = append(ret, + fmt.Sprintf(" Name: %s", *in.Name), + ) + } + if in.Description != nil { + ret = append(ret, + fmt.Sprintf(" Description: %s", *in.Description), + ) + } + + return base.WrapForHelpText(ret) +} diff --git a/internal/cmd/commands/scopes/project_create.go b/internal/cmd/commands/scopes/project_create.go index f7c49823f..117bb76ae 100644 --- a/internal/cmd/commands/scopes/project_create.go +++ b/internal/cmd/commands/scopes/project_create.go @@ -43,7 +43,7 @@ Usage: watchtower projects create } func (c *CreateProjectCommand) Flags() *base.FlagSets { - set := c.FlagSet(base.FlagSetHTTP | base.FlagSetOutputFormat) + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) f := set.NewFlagSet("Command Options") @@ -105,8 +105,11 @@ func (c *CreateProjectCommand) Run(args []string) int { case apiErr != nil: c.UI.Error(pretty.Sprint(apiErr)) return 2 - default: - c.UI.Info(pretty.Sprint(project)) + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(printProject(project)) } return 0 diff --git a/internal/cmd/commands/scopes/project_read.go b/internal/cmd/commands/scopes/project_read.go index b6202c1c2..87c31f604 100644 --- a/internal/cmd/commands/scopes/project_read.go +++ b/internal/cmd/commands/scopes/project_read.go @@ -43,7 +43,7 @@ Usage: watchtower projects read } func (c *ReadProjectCommand) Flags() *base.FlagSets { - set := c.FlagSet(base.FlagSetHTTP | base.FlagSetOutputFormat) + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) f := set.NewFlagSet("Command Options") @@ -97,8 +97,11 @@ func (c *ReadProjectCommand) Run(args []string) int { case apiErr != nil: c.UI.Error(pretty.Sprint(apiErr)) return 2 - default: - c.UI.Info(pretty.Sprint(project)) + } + + switch base.Format(c.UI) { + case "table": + c.UI.Output(printProject(project)) } return 0 diff --git a/internal/cmd/format.go b/internal/cmd/format.go deleted file mode 100644 index ae3b72aeb..000000000 --- a/internal/cmd/format.go +++ /dev/null @@ -1,34 +0,0 @@ -package cmd - -import ( - "os" - - "github.com/hashicorp/watchtower/internal/cmd/base" - "github.com/mitchellh/cli" -) - -type Formatter interface { - //Output(ui cli.Ui, secret *api.Secret, data interface{}) error - Format(data interface{}) ([]byte, error) -} - -var Formatters = map[string]Formatter{ - "json": base.JsonFormatter{}, - "table": base.TableFormatter{}, - "yaml": base.YamlFormatter{}, - "yml": base.YamlFormatter{}, -} - -func Format(ui cli.Ui) string { - switch ui.(type) { - case *WatchtowerUI: - return ui.(*WatchtowerUI).format - } - - format := os.Getenv(base.EnvWatchtowerCLIFormat) - if format == "" { - format = "table" - } - - return format -} diff --git a/internal/cmd/main.go b/internal/cmd/main.go index 0b6c8210e..8b7346c9e 100644 --- a/internal/cmd/main.go +++ b/internal/cmd/main.go @@ -18,11 +18,6 @@ import ( "github.com/mitchellh/cli" ) -type WatchtowerUI struct { - cli.Ui - format string -} - // setupEnv parses args and may replace them and sets some env vars to known // values based on format options func setupEnv(args []string) (retArgs []string, format string, outputCurlString bool) { @@ -128,7 +123,7 @@ func RunCustom(args []string, runOpts *RunOptions) int { uiErrWriter = ioutil.Discard } - ui := &WatchtowerUI{ + ui := &base.WatchtowerUI{ Ui: &cli.ColoredUi{ ErrorColor: cli.UiColorRed, WarnColor: cli.UiColorYellow, @@ -138,10 +133,10 @@ func RunCustom(args []string, runOpts *RunOptions) int { ErrorWriter: uiErrWriter, }, }, - format: format, + Format: format, } - serverCmdUi := &WatchtowerUI{ + serverCmdUi := &base.WatchtowerUI{ Ui: &cli.ColoredUi{ ErrorColor: cli.UiColorRed, WarnColor: cli.UiColorYellow, @@ -150,10 +145,10 @@ func RunCustom(args []string, runOpts *RunOptions) int { Writer: runOpts.Stdout, }, }, - format: format, + Format: format, } - if _, ok := Formatters[format]; !ok { + if _, ok := base.Formatters[format]; !ok { ui.Error(fmt.Sprintf("Invalid output format: %s", format)) return 1 } diff --git a/internal/db/domains_test.go b/internal/db/domains_test.go index 4ce328d49..d20963015 100644 --- a/internal/db/domains_test.go +++ b/internal/db/domains_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestDomain_PublicPrivateId(t *testing.T) { +func TestDomain_Ids(t *testing.T) { const ( createTableBase = ` create table if not exists test_table_{{rep}}_id ( @@ -29,7 +29,7 @@ returning id; conn, _ := TestSetup(t, "postgres") db := conn.DB() - for _, typ := range []string{"public", "private"} { + for _, typ := range []string{"public", "private", "role", "scope"} { createTable := strings.Replace(createTableBase, "{{rep}}", typ, -1) insert := strings.Replace(insertBase, "{{rep}}", typ, -1) @@ -37,17 +37,44 @@ returning id; t.Fatalf("query: \n%s\n error: %s", createTable, err) } - failTests := []string{ - " ", - "bar", - "00000009", + failTests := []struct { + value string + exception string + }{ + { + " ", + "", + }, + { + "bar", + "", + }, + { + "00000009", + "", + }, + { + "r_default", + "role", + }, + { + "global", + "scope", + }, } for _, tt := range failTests { - value := tt - t.Run(tt, func(t *testing.T) { - t.Logf("insert value: %q", value) - if _, err := db.Query(insert, value); err == nil { - t.Errorf("want error, got no error for inserting public_id: %s", value) + t.Run(tt.value, func(t *testing.T) { + t.Logf("insert value: %q", tt.value) + _, err := db.Query(insert, tt.value) + switch { + case err == nil && tt.exception != typ: + t.Errorf("want error, got no error for inserting public_id: %s", tt.value) + case err == nil && tt.exception == typ: + // All good + case err != nil && tt.exception != typ: + // All good + default: + t.Fatal("unhandled") } }) } @@ -345,3 +372,32 @@ returning public_id; require.NoError(err) assert.Equal(3, v) } + +func TestDomain_DisallowDefaultRoleModification(t *testing.T) { + conn, _ := TestSetup(t, "postgres") + db := conn.DB() + + _, err := db.Exec(`delete from iam_role where public_id = $1`, "r_default") + require.Error(t, err) + assert.Equal(t, `pq: deletion of default role not allowed`, err.Error()) + + // This actually tests not just default, but all roles + _, err = db.Exec(`update iam_role set public_id = $1 where public_id = $2`, "r_something", "r_default") + require.Error(t, err) + assert.Equal(t, `pq: update of role primary key not allowed`, err.Error()) +} + +func TestDomain_DefaultUsersExist(t *testing.T) { + conn, _ := TestSetup(t, "postgres") + db := conn.DB() + + for _, val := range []string{"u_anon", "u_auth"} { + rows, err := db.Query(`select from iam_user where public_id = $1`, val) + require.NoError(t, err) + var count int + for rows.Next() { + count++ + } + assert.Equal(t, 1, count) + } +} diff --git a/internal/db/migrations/postgres.gen.go b/internal/db/migrations/postgres.gen.go index b8678a317..059a63b08 100644 --- a/internal/db/migrations/postgres.gen.go +++ b/internal/db/migrations/postgres.gen.go @@ -59,6 +59,13 @@ check( comment on domain wt_scope_id is '"u_anon", "u_auth", or random ID generated with github.com/hashicorp/vault/sdk/helper/base62'; +create domain wt_role_id as text +check( + length(trim(value)) > 10 or value = 'r_default' +); +comment on domain wt_scope_id is +'"r_default", or random ID generated with github.com/hashicorp/vault/sdk/helper/base62'; + create domain wt_timestamp as timestamp with time zone default current_timestamp; @@ -453,7 +460,7 @@ drop table iam_role_grant cascade; drop function iam_sub_names cascade; drop function iam_immutable_scope_type_func cascade; drop function iam_sub_scopes_func cascade; -drop function iam_immutable_role cascade; +drop function iam_immutable_role_principal cascade; drop function iam_user_role_scope_check cascade; drop function iam_group_role_scope_check cascade; drop function iam_group_member_scope_check cascade; @@ -814,7 +821,7 @@ insert into iam_user (public_id, name, description, scope_id) values ('u_auth', 'authenticated', 'The authenticated user matches any user that has a valid token', 'global'); create table iam_role ( - public_id wt_public_id primary key, + public_id wt_role_id primary key, create_time wt_timestamp, update_time wt_timestamp, name text, @@ -837,7 +844,7 @@ create table iam_role ( -- Grants are immutable, which is enforced via the trigger below create table iam_role_grant ( create_time wt_timestamp, - role_id wt_public_id -- pk + role_id wt_role_id -- pk references iam_role(public_id) on delete cascade on update cascade, @@ -907,6 +914,51 @@ before insert or update on iam_role for each row execute procedure grant_scope_id_valid(); +-- disallow_default_role_deletion prevents the default role (r_default) from +-- being deleted +create or replace function + disallow_default_role_deletion() + returns trigger +as $$ +begin + if old.public_id = 'r_default' then + raise exception 'deletion of default role not allowed'; + end if; + return old; +end; +$$ language plpgsql; + +create trigger + disallow_default_role_deletion +before +delete on iam_role + for each row execute procedure disallow_default_role_deletion(); + +-- immutable_default_role_id prevents the a role's id from being updated. +create or replace function + immutable_role_id() + returns trigger +as $$ +begin + if new.public_id is distinct from old.public_id then + raise exception 'update of role primary key not allowed'; + end if; + return new; +end; +$$ language plpgsql; + +create trigger + immutable_role_id +before +update on iam_role + for each row execute procedure immutable_role_id(); + +insert into iam_role (public_id, name, description, scope_id) + values('r_default', 'default', 'default role', 'global'); + +insert into iam_role_grant (role_id, canonical_grant, raw_grant) + values('r_default', 'type=org;actions=list', 'type=org;actions=list'); + create table iam_group ( public_id wt_public_id not null primary key, create_time wt_timestamp, @@ -953,10 +1005,11 @@ update on iam_group -- iam_user_role contains roles that have been assigned to users. Users can be -- from any scope. The rows in this table must be immutable after insert, which --- will be ensured with a before update trigger using iam_immutable_role(). +-- will be ensured with a before update trigger using +-- iam_immutable_role_principal(). create table iam_user_role ( create_time wt_timestamp, - role_id wt_public_id + role_id wt_role_id references iam_role(public_id) on delete cascade on update cascade, @@ -970,10 +1023,10 @@ create table iam_user_role ( -- iam_group_role contains roles that have been assigned to groups. -- Groups can be from any scope. The rows in this table must be immutable after -- insert, which will be ensured with a before update trigger using --- iam_immutable_role(). +-- iam_immutable_role_principal(). create table iam_group_role ( create_time wt_timestamp, - role_id wt_public_id + role_id wt_role_id references iam_role(public_id) on delete cascade on update cascade, @@ -997,6 +1050,12 @@ begin end; $$ language plpgsql; +insert into iam_user_role (role_id, principal_id) + values + ('r_default', 'u_anon'), + ('r_default', 'u_auth'); + + -- iam_principle_role provides a consolidated view all principal roles assigned -- (user and group roles). create view iam_principal_role as @@ -1032,9 +1091,9 @@ where gr.role_id = r.public_id and g.public_id = gr.principal_id; --- iam_immutable_role() ensures that roles assigned to principals are immutable. +-- iam_immutable_role_principal() ensures that roles assigned to principals are immutable. create or replace function - iam_immutable_role() + iam_immutable_role_principal() returns trigger as $$ begin @@ -1042,10 +1101,10 @@ begin end; $$ language plpgsql; -create trigger immutable_role +create trigger immutable_role_principal before update on iam_user_role - for each row execute procedure iam_immutable_role(); + for each row execute procedure iam_immutable_role_principal(); create trigger immutable_create_time @@ -1059,10 +1118,10 @@ before insert on iam_user_role for each row execute procedure default_create_time(); -create trigger immutable_role +create trigger immutable_role_principal before update on iam_group_role - for each row execute procedure iam_immutable_role(); + for each row execute procedure iam_immutable_role_principal(); create trigger immutable_create_time diff --git a/internal/db/migrations/postgres/01_domain_types.up.sql b/internal/db/migrations/postgres/01_domain_types.up.sql index d85c77979..825f681cf 100644 --- a/internal/db/migrations/postgres/01_domain_types.up.sql +++ b/internal/db/migrations/postgres/01_domain_types.up.sql @@ -28,6 +28,13 @@ check( comment on domain wt_scope_id is '"u_anon", "u_auth", or random ID generated with github.com/hashicorp/vault/sdk/helper/base62'; +create domain wt_role_id as text +check( + length(trim(value)) > 10 or value = 'r_default' +); +comment on domain wt_scope_id is +'"r_default", or random ID generated with github.com/hashicorp/vault/sdk/helper/base62'; + create domain wt_timestamp as timestamp with time zone default current_timestamp; diff --git a/internal/db/migrations/postgres/06_iam.down.sql b/internal/db/migrations/postgres/06_iam.down.sql index a34992081..d25c2a7b5 100644 --- a/internal/db/migrations/postgres/06_iam.down.sql +++ b/internal/db/migrations/postgres/06_iam.down.sql @@ -18,7 +18,7 @@ drop table iam_role_grant cascade; drop function iam_sub_names cascade; drop function iam_immutable_scope_type_func cascade; drop function iam_sub_scopes_func cascade; -drop function iam_immutable_role cascade; +drop function iam_immutable_role_principal cascade; drop function iam_user_role_scope_check cascade; drop function iam_group_role_scope_check cascade; drop function iam_group_member_scope_check cascade; diff --git a/internal/db/migrations/postgres/06_iam.up.sql b/internal/db/migrations/postgres/06_iam.up.sql index 4894665d6..04db35c26 100644 --- a/internal/db/migrations/postgres/06_iam.up.sql +++ b/internal/db/migrations/postgres/06_iam.up.sql @@ -339,7 +339,7 @@ insert into iam_user (public_id, name, description, scope_id) values ('u_auth', 'authenticated', 'The authenticated user matches any user that has a valid token', 'global'); create table iam_role ( - public_id wt_public_id primary key, + public_id wt_role_id primary key, create_time wt_timestamp, update_time wt_timestamp, name text, @@ -362,7 +362,7 @@ create table iam_role ( -- Grants are immutable, which is enforced via the trigger below create table iam_role_grant ( create_time wt_timestamp, - role_id wt_public_id -- pk + role_id wt_role_id -- pk references iam_role(public_id) on delete cascade on update cascade, @@ -432,6 +432,51 @@ before insert or update on iam_role for each row execute procedure grant_scope_id_valid(); +-- disallow_default_role_deletion prevents the default role (r_default) from +-- being deleted +create or replace function + disallow_default_role_deletion() + returns trigger +as $$ +begin + if old.public_id = 'r_default' then + raise exception 'deletion of default role not allowed'; + end if; + return old; +end; +$$ language plpgsql; + +create trigger + disallow_default_role_deletion +before +delete on iam_role + for each row execute procedure disallow_default_role_deletion(); + +-- immutable_default_role_id prevents the a role's id from being updated. +create or replace function + immutable_role_id() + returns trigger +as $$ +begin + if new.public_id is distinct from old.public_id then + raise exception 'update of role primary key not allowed'; + end if; + return new; +end; +$$ language plpgsql; + +create trigger + immutable_role_id +before +update on iam_role + for each row execute procedure immutable_role_id(); + +insert into iam_role (public_id, name, description, scope_id) + values('r_default', 'default', 'default role', 'global'); + +insert into iam_role_grant (role_id, canonical_grant, raw_grant) + values('r_default', 'type=org;actions=list', 'type=org;actions=list'); + create table iam_group ( public_id wt_public_id not null primary key, create_time wt_timestamp, @@ -478,10 +523,11 @@ update on iam_group -- iam_user_role contains roles that have been assigned to users. Users can be -- from any scope. The rows in this table must be immutable after insert, which --- will be ensured with a before update trigger using iam_immutable_role(). +-- will be ensured with a before update trigger using +-- iam_immutable_role_principal(). create table iam_user_role ( create_time wt_timestamp, - role_id wt_public_id + role_id wt_role_id references iam_role(public_id) on delete cascade on update cascade, @@ -495,10 +541,10 @@ create table iam_user_role ( -- iam_group_role contains roles that have been assigned to groups. -- Groups can be from any scope. The rows in this table must be immutable after -- insert, which will be ensured with a before update trigger using --- iam_immutable_role(). +-- iam_immutable_role_principal(). create table iam_group_role ( create_time wt_timestamp, - role_id wt_public_id + role_id wt_role_id references iam_role(public_id) on delete cascade on update cascade, @@ -522,6 +568,12 @@ begin end; $$ language plpgsql; +insert into iam_user_role (role_id, principal_id) + values + ('r_default', 'u_anon'), + ('r_default', 'u_auth'); + + -- iam_principle_role provides a consolidated view all principal roles assigned -- (user and group roles). create view iam_principal_role as @@ -557,9 +609,9 @@ where gr.role_id = r.public_id and g.public_id = gr.principal_id; --- iam_immutable_role() ensures that roles assigned to principals are immutable. +-- iam_immutable_role_principal() ensures that roles assigned to principals are immutable. create or replace function - iam_immutable_role() + iam_immutable_role_principal() returns trigger as $$ begin @@ -567,10 +619,10 @@ begin end; $$ language plpgsql; -create trigger immutable_role +create trigger immutable_role_principal before update on iam_user_role - for each row execute procedure iam_immutable_role(); + for each row execute procedure iam_immutable_role_principal(); create trigger immutable_create_time @@ -584,10 +636,10 @@ before insert on iam_user_role for each row execute procedure default_create_time(); -create trigger immutable_role +create trigger immutable_role_principal before update on iam_group_role - for each row execute procedure iam_immutable_role(); + for each row execute procedure iam_immutable_role_principal(); create trigger immutable_create_time diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 5f7ded11a..292d8d7c7 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -72,6 +72,46 @@ ] } }, + "/v1/orgs/{org_id}/auth-methods/{auth_method_id}:authenticate": { + "post": { + "summary": "Authenticate a user to an Org and retrieve an authentication token.", + "operationId": "AuthenticationService_Authenticate", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.authtokens.v1.AuthToken" + } + } + }, + "parameters": [ + { + "name": "org_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "auth_method_id", + "description": "The id to the authmethod in the system being used for authentication. The auth method must be in the org\nbeing logged in to.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.services.v1.AuthenticateRequest" + } + } + ], + "tags": [ + "controller.api.services.v1.AuthenticationService" + ] + } + }, "/v1/orgs/{org_id}/groups": { "get": { "summary": "Lists all Groups", @@ -3051,43 +3091,10 @@ ] } }, - "/v1/orgs/{org_id}:authenticate": { - "post": { - "summary": "Authenticate a user to an Org and retrieve an authentication token.", - "operationId": "OrgService_Authenticate", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/controller.api.resources.authtokens.v1.AuthToken" - } - } - }, - "parameters": [ - { - "name": "org_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/controller.api.services.v1.AuthenticateRequest" - } - } - ], - "tags": [ - "controller.api.services.v1.OrgService" - ] - } - }, "/v1/orgs/{org_id}:deauthenticate": { "post": { "summary": "Deauthenticate an authentication token from an Org.", - "operationId": "OrgService_Deauthenticate", + "operationId": "AuthenticationService_Deauthenticate", "responses": { "200": { "description": "A successful response.", @@ -3105,7 +3112,7 @@ } ], "tags": [ - "controller.api.services.v1.OrgService" + "controller.api.services.v1.AuthenticationService" ] } } @@ -3339,6 +3346,26 @@ }, "title": "HostSet is a collection of Hosts created and managed by a HostCatalog" }, + "controller.api.resources.roles.v1.Principal": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the principal.\nOutput only.", + "readOnly": true + }, + "type": { + "type": "string", + "description": "The type of the principal.\nOutput only.", + "readOnly": true + }, + "scope_id": { + "type": "string", + "description": "The scope ID of the principal.\nOutput only.", + "readOnly": true + } + } + }, "controller.api.resources.roles.v1.Role": { "type": "object", "properties": { @@ -3382,20 +3409,20 @@ "description": "The version can be used in subsiquent write requests to ensure this resource\nhas not changed and to fail the write if it has.\nOutput only.", "readOnly": true }, - "user_ids": { + "principal_ids": { "type": "array", "items": { "type": "string" }, - "description": "The principals that are assigned this role.\nOutput only.", + "description": "The IDs (only) of principals that are assigned to this role.\nOutput only.", "readOnly": true }, - "group_ids": { + "principals": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/controller.api.resources.roles.v1.Principal" }, - "description": "Output only.", + "description": "The principals that are assigned to this role.\nOutput only.", "readOnly": true }, "grants": { @@ -3403,7 +3430,7 @@ "items": { "type": "string" }, - "description": "The grants that this role provides for it's principals.\nOutput only.", + "description": "The grants that this role provides for its principals.\nOutput only.", "readOnly": true }, "grants_canonical": { @@ -3578,13 +3605,7 @@ "format": "int64", "description": "The version ensures the role hasn't changed since it was last retrieved and if\nit has the request will fail." }, - "user_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "group_ids": { + "principal_ids": { "type": "array", "items": { "type": "string" @@ -3617,8 +3638,9 @@ "type": "string", "description": "This can be \"cookie\" or \"token\". If not provided, \"token\" will be used. For now only type \"token\" is returned." }, - "password_credential": { - "$ref": "#/definitions/controller.api.services.v1.PasswordCredential" + "credentials": { + "type": "object", + "description": "credentials are the different possible credential names depending on what type of auth method is used.\nFor password auth method: should include only \"name\" and \"password\"." } } }, @@ -3925,17 +3947,6 @@ } } }, - "controller.api.services.v1.PasswordCredential": { - "type": "object", - "properties": { - "login_name": { - "type": "string" - }, - "password": { - "type": "string" - } - } - }, "controller.api.services.v1.RemoveFromHostSetResponse": { "type": "object" }, @@ -3989,13 +4000,7 @@ "format": "int64", "description": "The version ensures the role hasn't changed since it was last retrieved and if\nit has the request will fail." }, - "user_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "group_ids": { + "principal_ids": { "type": "array", "items": { "type": "string" @@ -4061,13 +4066,7 @@ "format": "int64", "description": "The version ensures the role hasn't changed since it was last retrieved and if\nit has the request will fail." }, - "user_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "group_ids": { + "principal_ids": { "type": "array", "items": { "type": "string" diff --git a/internal/gen/controller/api/resources/roles/role.pb.go b/internal/gen/controller/api/resources/roles/role.pb.go index aaf430419..e836198f8 100644 --- a/internal/gen/controller/api/resources/roles/role.pb.go +++ b/internal/gen/controller/api/resources/roles/role.pb.go @@ -27,6 +27,113 @@ const ( // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 +type Principal struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The ID of the principal. + // Output only. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // The type of the principal. + // Output only. + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + // The scope ID of the principal. + // Output only. + ScopeId string `protobuf:"bytes,3,opt,name=scope_id,proto3" json:"scope_id,omitempty"` +} + +func (x *Principal) Reset() { + *x = Principal{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_resources_roles_v1_role_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Principal) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Principal) ProtoMessage() {} + +func (x *Principal) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_resources_roles_v1_role_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Principal.ProtoReflect.Descriptor instead. +func (*Principal) Descriptor() ([]byte, []int) { + return file_controller_api_resources_roles_v1_role_proto_rawDescGZIP(), []int{0} +} + +func (x *Principal) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Principal) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Principal) GetScopeId() string { + if x != nil { + return x.ScopeId + } + return "" +} + +type Grant struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Grant) Reset() { + *x = Grant{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_resources_roles_v1_role_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Grant) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Grant) ProtoMessage() {} + +func (x *Grant) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_resources_roles_v1_role_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Grant.ProtoReflect.Descriptor instead. +func (*Grant) Descriptor() ([]byte, []int) { + return file_controller_api_resources_roles_v1_role_proto_rawDescGZIP(), []int{1} +} + // Role contains all fields related to a Role resource type Role struct { state protoimpl.MessageState @@ -58,12 +165,13 @@ type Role struct { // has not changed and to fail the write if it has. // Output only. Version uint32 `protobuf:"varint,8,opt,name=version,proto3" json:"version,omitempty"` - // The principals that are assigned this role. + // The IDs (only) of principals that are assigned to this role. // Output only. - UserIds []string `protobuf:"bytes,9,rep,name=user_ids,json=userIds,proto3" json:"user_ids,omitempty"` + PrincipalIds []string `protobuf:"bytes,9,rep,name=principal_ids,proto3" json:"principal_ids,omitempty"` + // The principals that are assigned to this role. // Output only. - GroupIds []string `protobuf:"bytes,10,rep,name=group_ids,json=groupIds,proto3" json:"group_ids,omitempty"` - // The grants that this role provides for it's principals. + Principals []*Principal `protobuf:"bytes,10,rep,name=principals,proto3" json:"principals,omitempty"` + // The grants that this role provides for its principals. // Output only. Grants []string `protobuf:"bytes,11,rep,name=grants,proto3" json:"grants,omitempty"` // The canonical version of the grants in the grants field with the same index. @@ -77,7 +185,7 @@ type Role struct { func (x *Role) Reset() { *x = Role{} if protoimpl.UnsafeEnabled { - mi := &file_controller_api_resources_roles_v1_role_proto_msgTypes[0] + mi := &file_controller_api_resources_roles_v1_role_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -90,7 +198,7 @@ func (x *Role) String() string { func (*Role) ProtoMessage() {} func (x *Role) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_resources_roles_v1_role_proto_msgTypes[0] + mi := &file_controller_api_resources_roles_v1_role_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -103,7 +211,7 @@ func (x *Role) ProtoReflect() protoreflect.Message { // Deprecated: Use Role.ProtoReflect.Descriptor instead. func (*Role) Descriptor() ([]byte, []int) { - return file_controller_api_resources_roles_v1_role_proto_rawDescGZIP(), []int{0} + return file_controller_api_resources_roles_v1_role_proto_rawDescGZIP(), []int{2} } func (x *Role) GetId() string { @@ -162,16 +270,16 @@ func (x *Role) GetVersion() uint32 { return 0 } -func (x *Role) GetUserIds() []string { +func (x *Role) GetPrincipalIds() []string { if x != nil { - return x.UserIds + return x.PrincipalIds } return nil } -func (x *Role) GetGroupIds() []string { +func (x *Role) GetPrincipals() []*Principal { if x != nil { - return x.GroupIds + return x.Principals } return nil } @@ -209,48 +317,57 @@ var file_controller_api_resources_roles_v1_role_proto_rawDesc = []byte{ 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xbc, 0x04, 0x0a, 0x04, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, - 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, - 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x36, 0x0a, - 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x44, 0x0a, 0x0e, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, - 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x67, 0x72, 0x61, - 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, - 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0a, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x16, 0x0a, - 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, - 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x5f, - 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0f, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, - 0x12, 0x1f, 0x0a, 0x0b, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, - 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x4a, 0x73, 0x6f, - 0x6e, 0x42, 0x53, 0x5a, 0x51, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x74, - 0x6f, 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, - 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, - 0x3b, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x6f, 0x22, 0x4b, 0x0a, 0x09, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x22, + 0x07, 0x0a, 0x05, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x22, 0xf8, 0x04, 0x0a, 0x04, 0x52, 0x6f, 0x6c, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x30, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x44, 0x0a, 0x0e, 0x67, + 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x0e, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x70, + 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, + 0x73, 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x18, + 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, + 0x70, 0x61, 0x6c, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x67, 0x72, 0x61, 0x6e, 0x74, + 0x73, 0x5f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x18, 0x0c, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0f, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x43, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, + 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x5f, 0x6a, 0x73, 0x6f, + 0x6e, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x4a, + 0x73, 0x6f, 0x6e, 0x42, 0x53, 0x5a, 0x51, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, 0x61, 0x74, 0x63, + 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, + 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x72, 0x6f, 0x6c, + 0x65, 0x73, 0x3b, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -265,25 +382,28 @@ func file_controller_api_resources_roles_v1_role_proto_rawDescGZIP() []byte { return file_controller_api_resources_roles_v1_role_proto_rawDescData } -var file_controller_api_resources_roles_v1_role_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_api_resources_roles_v1_role_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_controller_api_resources_roles_v1_role_proto_goTypes = []interface{}{ - (*Role)(nil), // 0: controller.api.resources.roles.v1.Role - (*wrappers.StringValue)(nil), // 1: google.protobuf.StringValue - (*timestamp.Timestamp)(nil), // 2: google.protobuf.Timestamp - (*wrappers.BoolValue)(nil), // 3: google.protobuf.BoolValue + (*Principal)(nil), // 0: controller.api.resources.roles.v1.Principal + (*Grant)(nil), // 1: controller.api.resources.roles.v1.Grant + (*Role)(nil), // 2: controller.api.resources.roles.v1.Role + (*wrappers.StringValue)(nil), // 3: google.protobuf.StringValue + (*timestamp.Timestamp)(nil), // 4: google.protobuf.Timestamp + (*wrappers.BoolValue)(nil), // 5: google.protobuf.BoolValue } var file_controller_api_resources_roles_v1_role_proto_depIdxs = []int32{ - 1, // 0: controller.api.resources.roles.v1.Role.name:type_name -> google.protobuf.StringValue - 1, // 1: controller.api.resources.roles.v1.Role.description:type_name -> google.protobuf.StringValue - 2, // 2: controller.api.resources.roles.v1.Role.created_time:type_name -> google.protobuf.Timestamp - 2, // 3: controller.api.resources.roles.v1.Role.updated_time:type_name -> google.protobuf.Timestamp - 3, // 4: controller.api.resources.roles.v1.Role.disabled:type_name -> google.protobuf.BoolValue - 1, // 5: controller.api.resources.roles.v1.Role.grant_scope_id:type_name -> google.protobuf.StringValue - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 3, // 0: controller.api.resources.roles.v1.Role.name:type_name -> google.protobuf.StringValue + 3, // 1: controller.api.resources.roles.v1.Role.description:type_name -> google.protobuf.StringValue + 4, // 2: controller.api.resources.roles.v1.Role.created_time:type_name -> google.protobuf.Timestamp + 4, // 3: controller.api.resources.roles.v1.Role.updated_time:type_name -> google.protobuf.Timestamp + 5, // 4: controller.api.resources.roles.v1.Role.disabled:type_name -> google.protobuf.BoolValue + 3, // 5: controller.api.resources.roles.v1.Role.grant_scope_id:type_name -> google.protobuf.StringValue + 0, // 6: controller.api.resources.roles.v1.Role.principals:type_name -> controller.api.resources.roles.v1.Principal + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_controller_api_resources_roles_v1_role_proto_init() } @@ -293,6 +413,30 @@ func file_controller_api_resources_roles_v1_role_proto_init() { } if !protoimpl.UnsafeEnabled { file_controller_api_resources_roles_v1_role_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Principal); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_resources_roles_v1_role_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Grant); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_resources_roles_v1_role_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Role); i { case 0: return &v.state @@ -311,7 +455,7 @@ func file_controller_api_resources_roles_v1_role_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_api_resources_roles_v1_role_proto_rawDesc, NumEnums: 0, - NumMessages: 1, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/gen/controller/api/services/authenticate_service.pb.go b/internal/gen/controller/api/services/authenticate_service.pb.go new file mode 100644 index 000000000..68f5090e1 --- /dev/null +++ b/internal/gen/controller/api/services/authenticate_service.pb.go @@ -0,0 +1,626 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.24.0 +// protoc v3.12.3 +// source: controller/api/services/v1/authenticate_service.proto + +package services + +import ( + context "context" + proto "github.com/golang/protobuf/proto" + _struct "github.com/golang/protobuf/ptypes/struct" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" + authtokens "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/authtokens" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +// The layout of the struct for "credentials" field in AuthenticateRequest. This message isn't +// directly referenced anywhere but is used here to define the expected field names and types. +type PasswordCredentials struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` +} + +func (x *PasswordCredentials) Reset() { + *x = PasswordCredentials{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_authenticate_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PasswordCredentials) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PasswordCredentials) ProtoMessage() {} + +func (x *PasswordCredentials) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_authenticate_service_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PasswordCredentials.ProtoReflect.Descriptor instead. +func (*PasswordCredentials) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_authenticate_service_proto_rawDescGZIP(), []int{0} +} + +func (x *PasswordCredentials) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *PasswordCredentials) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type AuthenticateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OrgId string `protobuf:"bytes,1,opt,name=org_id,json=orgId,proto3" json:"org_id,omitempty"` + // The id to the authmethod in the system being used for authentication. The auth method must be in the org + // being logged in to. + AuthMethodId string `protobuf:"bytes,2,opt,name=auth_method_id,json=authMethodId,proto3" json:"auth_method_id,omitempty"` + // This can be "cookie" or "token". If not provided, "token" will be used. For now only type "token" is returned. + TokenType string `protobuf:"bytes,3,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` + // credentials are the different possible credential names depending on what type of auth method is used. + // For password auth method: should include only "name" and "password". + Credentials *_struct.Struct `protobuf:"bytes,8,opt,name=credentials,proto3" json:"credentials,omitempty"` +} + +func (x *AuthenticateRequest) Reset() { + *x = AuthenticateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_authenticate_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AuthenticateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AuthenticateRequest) ProtoMessage() {} + +func (x *AuthenticateRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_authenticate_service_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AuthenticateRequest.ProtoReflect.Descriptor instead. +func (*AuthenticateRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_authenticate_service_proto_rawDescGZIP(), []int{1} +} + +func (x *AuthenticateRequest) GetOrgId() string { + if x != nil { + return x.OrgId + } + return "" +} + +func (x *AuthenticateRequest) GetAuthMethodId() string { + if x != nil { + return x.AuthMethodId + } + return "" +} + +func (x *AuthenticateRequest) GetTokenType() string { + if x != nil { + return x.TokenType + } + return "" +} + +func (x *AuthenticateRequest) GetCredentials() *_struct.Struct { + if x != nil { + return x.Credentials + } + return nil +} + +type AuthenticateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *authtokens.AuthToken `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *AuthenticateResponse) Reset() { + *x = AuthenticateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_authenticate_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AuthenticateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AuthenticateResponse) ProtoMessage() {} + +func (x *AuthenticateResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_authenticate_service_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AuthenticateResponse.ProtoReflect.Descriptor instead. +func (*AuthenticateResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_authenticate_service_proto_rawDescGZIP(), []int{2} +} + +func (x *AuthenticateResponse) GetItem() *authtokens.AuthToken { + if x != nil { + return x.Item + } + return nil +} + +type DeauthenticateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OrgId string `protobuf:"bytes,1,opt,name=org_id,json=orgId,proto3" json:"org_id,omitempty"` +} + +func (x *DeauthenticateRequest) Reset() { + *x = DeauthenticateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_authenticate_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeauthenticateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeauthenticateRequest) ProtoMessage() {} + +func (x *DeauthenticateRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_authenticate_service_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeauthenticateRequest.ProtoReflect.Descriptor instead. +func (*DeauthenticateRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_authenticate_service_proto_rawDescGZIP(), []int{3} +} + +func (x *DeauthenticateRequest) GetOrgId() string { + if x != nil { + return x.OrgId + } + return "" +} + +type DeauthenticateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeauthenticateResponse) Reset() { + *x = DeauthenticateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_authenticate_service_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeauthenticateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeauthenticateResponse) ProtoMessage() {} + +func (x *DeauthenticateResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_authenticate_service_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeauthenticateResponse.ProtoReflect.Descriptor instead. +func (*DeauthenticateResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_authenticate_service_proto_rawDescGZIP(), []int{4} +} + +var File_controller_api_services_v1_authenticate_service_proto protoreflect.FileDescriptor + +var file_controller_api_services_v1_authenticate_service_proto_rawDesc = []byte{ + 0x0a, 0x35, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x1a, 0x2c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, + 0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x36, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x45, 0x0a, 0x13, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0xac, 0x01, 0x0a, + 0x13, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x61, + 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, + 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x39, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0b, + 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x5d, 0x0a, 0x14, 0x41, + 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x61, 0x75, 0x74, + 0x68, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x2e, 0x0a, 0x15, 0x44, 0x65, + 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, + 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xff, 0x03, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x89, + 0x02, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, + 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, + 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x95, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, 0x22, 0x3c, 0x2f, 0x76, 0x31, + 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x61, + 0x75, 0x74, 0x68, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x2f, 0x7b, 0x61, 0x75, 0x74, + 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x92, 0x41, 0x45, 0x12, 0x43, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, + 0x20, 0x4f, 0x72, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, + 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x12, 0xd9, 0x01, 0x0a, 0x0e, 0x44, + 0x65, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x31, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x61, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, + 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x60, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x20, 0x2f, 0x76, + 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x3a, + 0x64, 0x65, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x92, 0x41, + 0x35, 0x12, 0x33, 0x44, 0x65, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, + 0x6e, 0x20, 0x4f, 0x72, 0x67, 0x2e, 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, + 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_api_services_v1_authenticate_service_proto_rawDescOnce sync.Once + file_controller_api_services_v1_authenticate_service_proto_rawDescData = file_controller_api_services_v1_authenticate_service_proto_rawDesc +) + +func file_controller_api_services_v1_authenticate_service_proto_rawDescGZIP() []byte { + file_controller_api_services_v1_authenticate_service_proto_rawDescOnce.Do(func() { + file_controller_api_services_v1_authenticate_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_services_v1_authenticate_service_proto_rawDescData) + }) + return file_controller_api_services_v1_authenticate_service_proto_rawDescData +} + +var file_controller_api_services_v1_authenticate_service_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_controller_api_services_v1_authenticate_service_proto_goTypes = []interface{}{ + (*PasswordCredentials)(nil), // 0: controller.api.services.v1.PasswordCredentials + (*AuthenticateRequest)(nil), // 1: controller.api.services.v1.AuthenticateRequest + (*AuthenticateResponse)(nil), // 2: controller.api.services.v1.AuthenticateResponse + (*DeauthenticateRequest)(nil), // 3: controller.api.services.v1.DeauthenticateRequest + (*DeauthenticateResponse)(nil), // 4: controller.api.services.v1.DeauthenticateResponse + (*_struct.Struct)(nil), // 5: google.protobuf.Struct + (*authtokens.AuthToken)(nil), // 6: controller.api.resources.authtokens.v1.AuthToken +} +var file_controller_api_services_v1_authenticate_service_proto_depIdxs = []int32{ + 5, // 0: controller.api.services.v1.AuthenticateRequest.credentials:type_name -> google.protobuf.Struct + 6, // 1: controller.api.services.v1.AuthenticateResponse.item:type_name -> controller.api.resources.authtokens.v1.AuthToken + 1, // 2: controller.api.services.v1.AuthenticationService.Authenticate:input_type -> controller.api.services.v1.AuthenticateRequest + 3, // 3: controller.api.services.v1.AuthenticationService.Deauthenticate:input_type -> controller.api.services.v1.DeauthenticateRequest + 2, // 4: controller.api.services.v1.AuthenticationService.Authenticate:output_type -> controller.api.services.v1.AuthenticateResponse + 4, // 5: controller.api.services.v1.AuthenticationService.Deauthenticate:output_type -> controller.api.services.v1.DeauthenticateResponse + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_controller_api_services_v1_authenticate_service_proto_init() } +func file_controller_api_services_v1_authenticate_service_proto_init() { + if File_controller_api_services_v1_authenticate_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_api_services_v1_authenticate_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PasswordCredentials); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_authenticate_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AuthenticateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_authenticate_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AuthenticateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_authenticate_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeauthenticateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_authenticate_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeauthenticateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_api_services_v1_authenticate_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_controller_api_services_v1_authenticate_service_proto_goTypes, + DependencyIndexes: file_controller_api_services_v1_authenticate_service_proto_depIdxs, + MessageInfos: file_controller_api_services_v1_authenticate_service_proto_msgTypes, + }.Build() + File_controller_api_services_v1_authenticate_service_proto = out.File + file_controller_api_services_v1_authenticate_service_proto_rawDesc = nil + file_controller_api_services_v1_authenticate_service_proto_goTypes = nil + file_controller_api_services_v1_authenticate_service_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// AuthenticationServiceClient is the client API for AuthenticationService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type AuthenticationServiceClient interface { + // Authenticate validates credentials provided and returns an auth token. + Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*AuthenticateResponse, error) + // Logout terminates a user's current session. + Deauthenticate(ctx context.Context, in *DeauthenticateRequest, opts ...grpc.CallOption) (*DeauthenticateResponse, error) +} + +type authenticationServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewAuthenticationServiceClient(cc grpc.ClientConnInterface) AuthenticationServiceClient { + return &authenticationServiceClient{cc} +} + +func (c *authenticationServiceClient) Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*AuthenticateResponse, error) { + out := new(AuthenticateResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.AuthenticationService/Authenticate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authenticationServiceClient) Deauthenticate(ctx context.Context, in *DeauthenticateRequest, opts ...grpc.CallOption) (*DeauthenticateResponse, error) { + out := new(DeauthenticateResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.AuthenticationService/Deauthenticate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AuthenticationServiceServer is the server API for AuthenticationService service. +type AuthenticationServiceServer interface { + // Authenticate validates credentials provided and returns an auth token. + Authenticate(context.Context, *AuthenticateRequest) (*AuthenticateResponse, error) + // Logout terminates a user's current session. + Deauthenticate(context.Context, *DeauthenticateRequest) (*DeauthenticateResponse, error) +} + +// UnimplementedAuthenticationServiceServer can be embedded to have forward compatible implementations. +type UnimplementedAuthenticationServiceServer struct { +} + +func (*UnimplementedAuthenticationServiceServer) Authenticate(context.Context, *AuthenticateRequest) (*AuthenticateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Authenticate not implemented") +} +func (*UnimplementedAuthenticationServiceServer) Deauthenticate(context.Context, *DeauthenticateRequest) (*DeauthenticateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Deauthenticate not implemented") +} + +func RegisterAuthenticationServiceServer(s *grpc.Server, srv AuthenticationServiceServer) { + s.RegisterService(&_AuthenticationService_serviceDesc, srv) +} + +func _AuthenticationService_Authenticate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AuthenticateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthenticationServiceServer).Authenticate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.AuthenticationService/Authenticate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthenticationServiceServer).Authenticate(ctx, req.(*AuthenticateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthenticationService_Deauthenticate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeauthenticateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthenticationServiceServer).Deauthenticate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.AuthenticationService/Deauthenticate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthenticationServiceServer).Deauthenticate(ctx, req.(*DeauthenticateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _AuthenticationService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "controller.api.services.v1.AuthenticationService", + HandlerType: (*AuthenticationServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Authenticate", + Handler: _AuthenticationService_Authenticate_Handler, + }, + { + MethodName: "Deauthenticate", + Handler: _AuthenticationService_Deauthenticate_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "controller/api/services/v1/authenticate_service.proto", +} diff --git a/internal/gen/controller/api/services/authenticate_service.pb.gw.go b/internal/gen/controller/api/services/authenticate_service.pb.gw.go new file mode 100644 index 000000000..30fca71f6 --- /dev/null +++ b/internal/gen/controller/api/services/authenticate_service.pb.gw.go @@ -0,0 +1,328 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: controller/api/services/v1/authenticate_service.proto + +/* +Package services is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package services + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage + +func request_AuthenticationService_Authenticate_0(ctx context.Context, marshaler runtime.Marshaler, client AuthenticationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AuthenticateRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + val, ok = pathParams["auth_method_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "auth_method_id") + } + + protoReq.AuthMethodId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "auth_method_id", err) + } + + msg, err := client.Authenticate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_AuthenticationService_Authenticate_0(ctx context.Context, marshaler runtime.Marshaler, server AuthenticationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AuthenticateRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + val, ok = pathParams["auth_method_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "auth_method_id") + } + + protoReq.AuthMethodId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "auth_method_id", err) + } + + msg, err := server.Authenticate(ctx, &protoReq) + return msg, metadata, err + +} + +func request_AuthenticationService_Deauthenticate_0(ctx context.Context, marshaler runtime.Marshaler, client AuthenticationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeauthenticateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + msg, err := client.Deauthenticate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_AuthenticationService_Deauthenticate_0(ctx context.Context, marshaler runtime.Marshaler, server AuthenticationServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeauthenticateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + msg, err := server.Deauthenticate(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterAuthenticationServiceHandlerServer registers the http handlers for service AuthenticationService to "mux". +// UnaryRPC :call AuthenticationServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +func RegisterAuthenticationServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server AuthenticationServiceServer) error { + + mux.Handle("POST", pattern_AuthenticationService_Authenticate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_AuthenticationService_Authenticate_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AuthenticationService_Authenticate_0(ctx, mux, outboundMarshaler, w, req, response_AuthenticationService_Authenticate_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_AuthenticationService_Deauthenticate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_AuthenticationService_Deauthenticate_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AuthenticationService_Deauthenticate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterAuthenticationServiceHandlerFromEndpoint is same as RegisterAuthenticationServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterAuthenticationServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterAuthenticationServiceHandler(ctx, mux, conn) +} + +// RegisterAuthenticationServiceHandler registers the http handlers for service AuthenticationService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterAuthenticationServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterAuthenticationServiceHandlerClient(ctx, mux, NewAuthenticationServiceClient(conn)) +} + +// RegisterAuthenticationServiceHandlerClient registers the http handlers for service AuthenticationService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "AuthenticationServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "AuthenticationServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "AuthenticationServiceClient" to call the correct interceptors. +func RegisterAuthenticationServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client AuthenticationServiceClient) error { + + mux.Handle("POST", pattern_AuthenticationService_Authenticate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_AuthenticationService_Authenticate_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AuthenticationService_Authenticate_0(ctx, mux, outboundMarshaler, w, req, response_AuthenticationService_Authenticate_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_AuthenticationService_Deauthenticate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_AuthenticationService_Deauthenticate_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AuthenticationService_Deauthenticate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +type response_AuthenticationService_Authenticate_0 struct { + proto.Message +} + +func (m response_AuthenticationService_Authenticate_0) XXX_ResponseBody() interface{} { + response := m.Message.(*AuthenticateResponse) + return response.Item +} + +var ( + pattern_AuthenticationService_Authenticate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "orgs", "org_id", "auth-methods", "auth_method_id"}, "authenticate", runtime.AssumeColonVerbOpt(true))) + + pattern_AuthenticationService_Deauthenticate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "orgs", "org_id"}, "deauthenticate", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_AuthenticationService_Authenticate_0 = runtime.ForwardResponseMessage + + forward_AuthenticationService_Deauthenticate_0 = runtime.ForwardResponseMessage +) diff --git a/internal/gen/controller/api/services/org_service.pb.go b/internal/gen/controller/api/services/org_service.pb.go index 8ed02ce64..631b4210e 100644 --- a/internal/gen/controller/api/services/org_service.pb.go +++ b/internal/gen/controller/api/services/org_service.pb.go @@ -10,7 +10,6 @@ import ( context "context" proto "github.com/golang/protobuf/proto" _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" - authtokens "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/authtokens" scopes "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/scopes" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" @@ -229,267 +228,6 @@ func (x *ListOrgsResponse) GetItems() []*scopes.Org { return nil } -type PasswordCredential struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - LoginName string `protobuf:"bytes,1,opt,name=login_name,json=loginName,proto3" json:"login_name,omitempty"` - Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` -} - -func (x *PasswordCredential) Reset() { - *x = PasswordCredential{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_org_service_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PasswordCredential) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PasswordCredential) ProtoMessage() {} - -func (x *PasswordCredential) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_org_service_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PasswordCredential.ProtoReflect.Descriptor instead. -func (*PasswordCredential) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_org_service_proto_rawDescGZIP(), []int{4} -} - -func (x *PasswordCredential) GetLoginName() string { - if x != nil { - return x.LoginName - } - return "" -} - -func (x *PasswordCredential) GetPassword() string { - if x != nil { - return x.Password - } - return "" -} - -type AuthenticateRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - OrgId string `protobuf:"bytes,1,opt,name=org_id,json=orgId,proto3" json:"org_id,omitempty"` - // The id to the authmethod in the system being used for authentication. The auth method must be in the org - // being logged in to. - AuthMethodId string `protobuf:"bytes,2,opt,name=auth_method_id,json=authMethodId,proto3" json:"auth_method_id,omitempty"` - // This can be "cookie" or "token". If not provided, "token" will be used. For now only type "token" is returned. - TokenType string `protobuf:"bytes,3,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` - PasswordCredential *PasswordCredential `protobuf:"bytes,4,opt,name=password_credential,json=passwordCredential,proto3" json:"password_credential,omitempty"` -} - -func (x *AuthenticateRequest) Reset() { - *x = AuthenticateRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_org_service_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AuthenticateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AuthenticateRequest) ProtoMessage() {} - -func (x *AuthenticateRequest) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_org_service_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AuthenticateRequest.ProtoReflect.Descriptor instead. -func (*AuthenticateRequest) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_org_service_proto_rawDescGZIP(), []int{5} -} - -func (x *AuthenticateRequest) GetOrgId() string { - if x != nil { - return x.OrgId - } - return "" -} - -func (x *AuthenticateRequest) GetAuthMethodId() string { - if x != nil { - return x.AuthMethodId - } - return "" -} - -func (x *AuthenticateRequest) GetTokenType() string { - if x != nil { - return x.TokenType - } - return "" -} - -func (x *AuthenticateRequest) GetPasswordCredential() *PasswordCredential { - if x != nil { - return x.PasswordCredential - } - return nil -} - -type AuthenticateResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Item *authtokens.AuthToken `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` -} - -func (x *AuthenticateResponse) Reset() { - *x = AuthenticateResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_org_service_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AuthenticateResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AuthenticateResponse) ProtoMessage() {} - -func (x *AuthenticateResponse) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_org_service_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AuthenticateResponse.ProtoReflect.Descriptor instead. -func (*AuthenticateResponse) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_org_service_proto_rawDescGZIP(), []int{6} -} - -func (x *AuthenticateResponse) GetItem() *authtokens.AuthToken { - if x != nil { - return x.Item - } - return nil -} - -type DeauthenticateRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - OrgId string `protobuf:"bytes,1,opt,name=org_id,json=orgId,proto3" json:"org_id,omitempty"` -} - -func (x *DeauthenticateRequest) Reset() { - *x = DeauthenticateRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_org_service_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeauthenticateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeauthenticateRequest) ProtoMessage() {} - -func (x *DeauthenticateRequest) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_org_service_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeauthenticateRequest.ProtoReflect.Descriptor instead. -func (*DeauthenticateRequest) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_org_service_proto_rawDescGZIP(), []int{7} -} - -func (x *DeauthenticateRequest) GetOrgId() string { - if x != nil { - return x.OrgId - } - return "" -} - -type DeauthenticateResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *DeauthenticateResponse) Reset() { - *x = DeauthenticateResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_api_services_v1_org_service_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeauthenticateResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeauthenticateResponse) ProtoMessage() {} - -func (x *DeauthenticateResponse) ProtoReflect() protoreflect.Message { - mi := &file_controller_api_services_v1_org_service_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeauthenticateResponse.ProtoReflect.Descriptor instead. -func (*DeauthenticateResponse) Descriptor() ([]byte, []int) { - return file_controller_api_services_v1_org_service_proto_rawDescGZIP(), []int{8} -} - var File_controller_api_services_v1_org_service_proto protoreflect.FileDescriptor var file_controller_api_services_v1_org_service_proto_rawDesc = []byte{ @@ -505,103 +243,42 @@ var file_controller_api_services_v1_org_service_proto_rawDesc = []byte{ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2c, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x36, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x61, - 0x75, 0x74, 0x68, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x75, 0x74, - 0x68, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x33, 0x0a, 0x0d, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, - 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x76, 0x69, 0x65, - 0x77, 0x22, 0x4d, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, - 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x72, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x22, 0x25, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x22, 0x51, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4f, - 0x72, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x05, 0x69, - 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x4f, 0x72, 0x67, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x4f, 0x0a, 0x12, 0x50, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0xd2, 0x01, 0x0a, 0x13, - 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x75, - 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x64, - 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x5f, 0x0a, 0x13, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x63, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x63, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x33, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x22, 0x4d, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x4f, 0x72, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4f, + 0x72, 0x67, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x25, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, + 0x4f, 0x72, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x76, + 0x69, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x22, + 0x51, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x63, + 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x72, 0x67, 0x52, 0x05, 0x69, 0x74, 0x65, + 0x6d, 0x73, 0x32, 0xae, 0x02, 0x0a, 0x0a, 0x4f, 0x72, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x92, 0x01, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67, 0x12, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, - 0x72, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x12, 0x70, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x22, 0x5d, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, - 0x2e, 0x0a, 0x15, 0x44, 0x65, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x22, - 0x18, 0x0a, 0x16, 0x44, 0x65, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xf7, 0x05, 0x0a, 0x0a, 0x4f, 0x72, - 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x92, 0x01, 0x0a, 0x06, 0x47, 0x65, 0x74, - 0x4f, 0x72, 0x67, 0x12, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x15, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, - 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x13, 0x12, 0x11, 0x47, 0x65, 0x74, 0x73, - 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x4f, 0x72, 0x67, 0x12, 0x8a, 0x01, - 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x73, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, - 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x92, 0x41, 0x10, 0x12, 0x0e, 0x4c, 0x69, 0x73, 0x74, - 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x4f, 0x72, 0x67, 0x73, 0x12, 0xea, 0x01, 0x0a, 0x0c, 0x41, - 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, - 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, - 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, - 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, - 0x41, 0x45, 0x12, 0x43, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x20, 0x61, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x4f, 0x72, - 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x61, - 0x6e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x12, 0xd9, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x61, 0x75, - 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x31, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, - 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x61, 0x75, 0x74, - 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x60, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x6f, - 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x64, 0x65, 0x61, - 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x92, 0x41, 0x35, 0x12, 0x33, - 0x44, 0x65, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x61, - 0x6e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x6e, 0x20, 0x4f, - 0x72, 0x67, 0x2e, 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x0d, 0x2f, 0x76, 0x31, + 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x92, 0x41, 0x13, 0x12, 0x11, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, + 0x6c, 0x65, 0x20, 0x4f, 0x72, 0x67, 0x12, 0x8a, 0x01, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x4f, + 0x72, 0x67, 0x73, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x4f, 0x72, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, + 0x92, 0x41, 0x10, 0x12, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x4f, + 0x72, 0x67, 0x73, 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, @@ -621,38 +298,26 @@ func file_controller_api_services_v1_org_service_proto_rawDescGZIP() []byte { return file_controller_api_services_v1_org_service_proto_rawDescData } -var file_controller_api_services_v1_org_service_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_controller_api_services_v1_org_service_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_controller_api_services_v1_org_service_proto_goTypes = []interface{}{ - (*GetOrgRequest)(nil), // 0: controller.api.services.v1.GetOrgRequest - (*GetOrgResponse)(nil), // 1: controller.api.services.v1.GetOrgResponse - (*ListOrgsRequest)(nil), // 2: controller.api.services.v1.ListOrgsRequest - (*ListOrgsResponse)(nil), // 3: controller.api.services.v1.ListOrgsResponse - (*PasswordCredential)(nil), // 4: controller.api.services.v1.PasswordCredential - (*AuthenticateRequest)(nil), // 5: controller.api.services.v1.AuthenticateRequest - (*AuthenticateResponse)(nil), // 6: controller.api.services.v1.AuthenticateResponse - (*DeauthenticateRequest)(nil), // 7: controller.api.services.v1.DeauthenticateRequest - (*DeauthenticateResponse)(nil), // 8: controller.api.services.v1.DeauthenticateResponse - (*scopes.Org)(nil), // 9: controller.api.resources.scopes.v1.Org - (*authtokens.AuthToken)(nil), // 10: controller.api.resources.authtokens.v1.AuthToken + (*GetOrgRequest)(nil), // 0: controller.api.services.v1.GetOrgRequest + (*GetOrgResponse)(nil), // 1: controller.api.services.v1.GetOrgResponse + (*ListOrgsRequest)(nil), // 2: controller.api.services.v1.ListOrgsRequest + (*ListOrgsResponse)(nil), // 3: controller.api.services.v1.ListOrgsResponse + (*scopes.Org)(nil), // 4: controller.api.resources.scopes.v1.Org } var file_controller_api_services_v1_org_service_proto_depIdxs = []int32{ - 9, // 0: controller.api.services.v1.GetOrgResponse.item:type_name -> controller.api.resources.scopes.v1.Org - 9, // 1: controller.api.services.v1.ListOrgsResponse.items:type_name -> controller.api.resources.scopes.v1.Org - 4, // 2: controller.api.services.v1.AuthenticateRequest.password_credential:type_name -> controller.api.services.v1.PasswordCredential - 10, // 3: controller.api.services.v1.AuthenticateResponse.item:type_name -> controller.api.resources.authtokens.v1.AuthToken - 0, // 4: controller.api.services.v1.OrgService.GetOrg:input_type -> controller.api.services.v1.GetOrgRequest - 2, // 5: controller.api.services.v1.OrgService.ListOrgs:input_type -> controller.api.services.v1.ListOrgsRequest - 5, // 6: controller.api.services.v1.OrgService.Authenticate:input_type -> controller.api.services.v1.AuthenticateRequest - 7, // 7: controller.api.services.v1.OrgService.Deauthenticate:input_type -> controller.api.services.v1.DeauthenticateRequest - 1, // 8: controller.api.services.v1.OrgService.GetOrg:output_type -> controller.api.services.v1.GetOrgResponse - 3, // 9: controller.api.services.v1.OrgService.ListOrgs:output_type -> controller.api.services.v1.ListOrgsResponse - 6, // 10: controller.api.services.v1.OrgService.Authenticate:output_type -> controller.api.services.v1.AuthenticateResponse - 8, // 11: controller.api.services.v1.OrgService.Deauthenticate:output_type -> controller.api.services.v1.DeauthenticateResponse - 8, // [8:12] is the sub-list for method output_type - 4, // [4:8] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 4, // 0: controller.api.services.v1.GetOrgResponse.item:type_name -> controller.api.resources.scopes.v1.Org + 4, // 1: controller.api.services.v1.ListOrgsResponse.items:type_name -> controller.api.resources.scopes.v1.Org + 0, // 2: controller.api.services.v1.OrgService.GetOrg:input_type -> controller.api.services.v1.GetOrgRequest + 2, // 3: controller.api.services.v1.OrgService.ListOrgs:input_type -> controller.api.services.v1.ListOrgsRequest + 1, // 4: controller.api.services.v1.OrgService.GetOrg:output_type -> controller.api.services.v1.GetOrgResponse + 3, // 5: controller.api.services.v1.OrgService.ListOrgs:output_type -> controller.api.services.v1.ListOrgsResponse + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_controller_api_services_v1_org_service_proto_init() } @@ -709,66 +374,6 @@ func file_controller_api_services_v1_org_service_proto_init() { return nil } } - file_controller_api_services_v1_org_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PasswordCredential); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_services_v1_org_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AuthenticateRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_services_v1_org_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AuthenticateResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_services_v1_org_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeauthenticateRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_api_services_v1_org_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeauthenticateResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } } type x struct{} out := protoimpl.TypeBuilder{ @@ -776,7 +381,7 @@ func file_controller_api_services_v1_org_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_api_services_v1_org_service_proto_rawDesc, NumEnums: 0, - NumMessages: 9, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, @@ -808,10 +413,6 @@ type OrgServiceClient interface { GetOrg(ctx context.Context, in *GetOrgRequest, opts ...grpc.CallOption) (*GetOrgResponse, error) // ListOrgs returns a list of stored orgs which exist in watchtower. ListOrgs(ctx context.Context, in *ListOrgsRequest, opts ...grpc.CallOption) (*ListOrgsResponse, error) - // Authenticate validates credentials provided and returns an auth token. - Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*AuthenticateResponse, error) - // Logout terminates a user's current session. - Deauthenticate(ctx context.Context, in *DeauthenticateRequest, opts ...grpc.CallOption) (*DeauthenticateResponse, error) } type orgServiceClient struct { @@ -840,24 +441,6 @@ func (c *orgServiceClient) ListOrgs(ctx context.Context, in *ListOrgsRequest, op return out, nil } -func (c *orgServiceClient) Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*AuthenticateResponse, error) { - out := new(AuthenticateResponse) - err := c.cc.Invoke(ctx, "/controller.api.services.v1.OrgService/Authenticate", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *orgServiceClient) Deauthenticate(ctx context.Context, in *DeauthenticateRequest, opts ...grpc.CallOption) (*DeauthenticateResponse, error) { - out := new(DeauthenticateResponse) - err := c.cc.Invoke(ctx, "/controller.api.services.v1.OrgService/Deauthenticate", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - // OrgServiceServer is the server API for OrgService service. type OrgServiceServer interface { // GetOrg returns a stored Org if present. The provided request @@ -866,10 +449,6 @@ type OrgServiceServer interface { GetOrg(context.Context, *GetOrgRequest) (*GetOrgResponse, error) // ListOrgs returns a list of stored orgs which exist in watchtower. ListOrgs(context.Context, *ListOrgsRequest) (*ListOrgsResponse, error) - // Authenticate validates credentials provided and returns an auth token. - Authenticate(context.Context, *AuthenticateRequest) (*AuthenticateResponse, error) - // Logout terminates a user's current session. - Deauthenticate(context.Context, *DeauthenticateRequest) (*DeauthenticateResponse, error) } // UnimplementedOrgServiceServer can be embedded to have forward compatible implementations. @@ -882,12 +461,6 @@ func (*UnimplementedOrgServiceServer) GetOrg(context.Context, *GetOrgRequest) (* func (*UnimplementedOrgServiceServer) ListOrgs(context.Context, *ListOrgsRequest) (*ListOrgsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListOrgs not implemented") } -func (*UnimplementedOrgServiceServer) Authenticate(context.Context, *AuthenticateRequest) (*AuthenticateResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Authenticate not implemented") -} -func (*UnimplementedOrgServiceServer) Deauthenticate(context.Context, *DeauthenticateRequest) (*DeauthenticateResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Deauthenticate not implemented") -} func RegisterOrgServiceServer(s *grpc.Server, srv OrgServiceServer) { s.RegisterService(&_OrgService_serviceDesc, srv) @@ -929,42 +502,6 @@ func _OrgService_ListOrgs_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } -func _OrgService_Authenticate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AuthenticateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OrgServiceServer).Authenticate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/controller.api.services.v1.OrgService/Authenticate", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OrgServiceServer).Authenticate(ctx, req.(*AuthenticateRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OrgService_Deauthenticate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeauthenticateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OrgServiceServer).Deauthenticate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/controller.api.services.v1.OrgService/Deauthenticate", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OrgServiceServer).Deauthenticate(ctx, req.(*DeauthenticateRequest)) - } - return interceptor(ctx, in, info, handler) -} - var _OrgService_serviceDesc = grpc.ServiceDesc{ ServiceName: "controller.api.services.v1.OrgService", HandlerType: (*OrgServiceServer)(nil), @@ -977,14 +514,6 @@ var _OrgService_serviceDesc = grpc.ServiceDesc{ MethodName: "ListOrgs", Handler: _OrgService_ListOrgs_Handler, }, - { - MethodName: "Authenticate", - Handler: _OrgService_Authenticate_Handler, - }, - { - MethodName: "Deauthenticate", - Handler: _OrgService_Deauthenticate_Handler, - }, }, Streams: []grpc.StreamDesc{}, Metadata: "controller/api/services/v1/org_service.proto", diff --git a/internal/gen/controller/api/services/org_service.pb.gw.go b/internal/gen/controller/api/services/org_service.pb.gw.go index a7cf6a4a3..3ee2c30a0 100644 --- a/internal/gen/controller/api/services/org_service.pb.gw.go +++ b/internal/gen/controller/api/services/org_service.pb.gw.go @@ -139,130 +139,6 @@ func local_request_OrgService_ListOrgs_0(ctx context.Context, marshaler runtime. } -func request_OrgService_Authenticate_0(ctx context.Context, marshaler runtime.Marshaler, client OrgServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq AuthenticateRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["org_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") - } - - protoReq.OrgId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) - } - - msg, err := client.Authenticate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_OrgService_Authenticate_0(ctx context.Context, marshaler runtime.Marshaler, server OrgServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq AuthenticateRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["org_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") - } - - protoReq.OrgId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) - } - - msg, err := server.Authenticate(ctx, &protoReq) - return msg, metadata, err - -} - -func request_OrgService_Deauthenticate_0(ctx context.Context, marshaler runtime.Marshaler, client OrgServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq DeauthenticateRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["org_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") - } - - protoReq.OrgId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) - } - - msg, err := client.Deauthenticate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_OrgService_Deauthenticate_0(ctx context.Context, marshaler runtime.Marshaler, server OrgServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq DeauthenticateRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["org_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") - } - - protoReq.OrgId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) - } - - msg, err := server.Deauthenticate(ctx, &protoReq) - return msg, metadata, err - -} - // RegisterOrgServiceHandlerServer registers the http handlers for service OrgService to "mux". // UnaryRPC :call OrgServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -308,46 +184,6 @@ func RegisterOrgServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, }) - mux.Handle("POST", pattern_OrgService_Authenticate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_OrgService_Authenticate_0(rctx, inboundMarshaler, server, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_OrgService_Authenticate_0(ctx, mux, outboundMarshaler, w, req, response_OrgService_Authenticate_0{resp}, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_OrgService_Deauthenticate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_OrgService_Deauthenticate_0(rctx, inboundMarshaler, server, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_OrgService_Deauthenticate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - return nil } @@ -429,46 +265,6 @@ func RegisterOrgServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, }) - mux.Handle("POST", pattern_OrgService_Authenticate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_OrgService_Authenticate_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_OrgService_Authenticate_0(ctx, mux, outboundMarshaler, w, req, response_OrgService_Authenticate_0{resp}, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_OrgService_Deauthenticate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_OrgService_Deauthenticate_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_OrgService_Deauthenticate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - return nil } @@ -481,31 +277,14 @@ func (m response_OrgService_GetOrg_0) XXX_ResponseBody() interface{} { return response.Item } -type response_OrgService_Authenticate_0 struct { - proto.Message -} - -func (m response_OrgService_Authenticate_0) XXX_ResponseBody() interface{} { - response := m.Message.(*AuthenticateResponse) - return response.Item -} - var ( pattern_OrgService_GetOrg_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "orgs", "id"}, "", runtime.AssumeColonVerbOpt(true))) pattern_OrgService_ListOrgs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "orgs"}, "", runtime.AssumeColonVerbOpt(true))) - - pattern_OrgService_Authenticate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "orgs", "org_id"}, "authenticate", runtime.AssumeColonVerbOpt(true))) - - pattern_OrgService_Deauthenticate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "orgs", "org_id"}, "deauthenticate", runtime.AssumeColonVerbOpt(true))) ) var ( forward_OrgService_GetOrg_0 = runtime.ForwardResponseMessage forward_OrgService_ListOrgs_0 = runtime.ForwardResponseMessage - - forward_OrgService_Authenticate_0 = runtime.ForwardResponseMessage - - forward_OrgService_Deauthenticate_0 = runtime.ForwardResponseMessage ) diff --git a/internal/gen/controller/api/services/role_service.pb.go b/internal/gen/controller/api/services/role_service.pb.go index 7a1aa2c0b..884ab0890 100644 --- a/internal/gen/controller/api/services/role_service.pb.go +++ b/internal/gen/controller/api/services/role_service.pb.go @@ -627,9 +627,8 @@ type AddRolePrincipalsRequest struct { RoleId string `protobuf:"bytes,3,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty"` // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. - Version *wrappers.UInt32Value `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` - UserIds []string `protobuf:"bytes,5,rep,name=user_ids,proto3" json:"user_ids,omitempty"` - GroupIds []string `protobuf:"bytes,6,rep,name=group_ids,proto3" json:"group_ids,omitempty"` + Version *wrappers.UInt32Value `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + PrincipalIds []string `protobuf:"bytes,5,rep,name=principal_ids,proto3" json:"principal_ids,omitempty"` } func (x *AddRolePrincipalsRequest) Reset() { @@ -692,16 +691,9 @@ func (x *AddRolePrincipalsRequest) GetVersion() *wrappers.UInt32Value { return nil } -func (x *AddRolePrincipalsRequest) GetUserIds() []string { +func (x *AddRolePrincipalsRequest) GetPrincipalIds() []string { if x != nil { - return x.UserIds - } - return nil -} - -func (x *AddRolePrincipalsRequest) GetGroupIds() []string { - if x != nil { - return x.GroupIds + return x.PrincipalIds } return nil } @@ -763,9 +755,8 @@ type SetRolePrincipalsRequest struct { RoleId string `protobuf:"bytes,3,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty"` // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. - Version *wrappers.UInt32Value `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` - UserIds []string `protobuf:"bytes,5,rep,name=user_ids,proto3" json:"user_ids,omitempty"` - GroupIds []string `protobuf:"bytes,6,rep,name=group_ids,proto3" json:"group_ids,omitempty"` + Version *wrappers.UInt32Value `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + PrincipalIds []string `protobuf:"bytes,5,rep,name=principal_ids,proto3" json:"principal_ids,omitempty"` } func (x *SetRolePrincipalsRequest) Reset() { @@ -828,16 +819,9 @@ func (x *SetRolePrincipalsRequest) GetVersion() *wrappers.UInt32Value { return nil } -func (x *SetRolePrincipalsRequest) GetUserIds() []string { +func (x *SetRolePrincipalsRequest) GetPrincipalIds() []string { if x != nil { - return x.UserIds - } - return nil -} - -func (x *SetRolePrincipalsRequest) GetGroupIds() []string { - if x != nil { - return x.GroupIds + return x.PrincipalIds } return nil } @@ -899,9 +883,8 @@ type RemoveRolePrincipalsRequest struct { RoleId string `protobuf:"bytes,3,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty"` // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. - Version *wrappers.UInt32Value `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` - UserIds []string `protobuf:"bytes,5,rep,name=user_ids,proto3" json:"user_ids,omitempty"` - GroupIds []string `protobuf:"bytes,6,rep,name=group_ids,proto3" json:"group_ids,omitempty"` + Version *wrappers.UInt32Value `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + PrincipalIds []string `protobuf:"bytes,5,rep,name=principal_ids,proto3" json:"principal_ids,omitempty"` } func (x *RemoveRolePrincipalsRequest) Reset() { @@ -964,16 +947,9 @@ func (x *RemoveRolePrincipalsRequest) GetVersion() *wrappers.UInt32Value { return nil } -func (x *RemoveRolePrincipalsRequest) GetUserIds() []string { +func (x *RemoveRolePrincipalsRequest) GetPrincipalIds() []string { if x != nil { - return x.UserIds - } - return nil -} - -func (x *RemoveRolePrincipalsRequest) GetGroupIds() []string { - if x != nil { - return x.GroupIds + return x.PrincipalIds } return nil } @@ -1493,7 +1469,7 @@ var file_controller_api_services_v1_role_service_proto_rawDesc = []byte{ 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2e, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x65, 0x78, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0xdb, 0x01, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x52, + 0x65, 0x78, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0xc7, 0x01, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, @@ -1503,311 +1479,307 @@ var file_controller_api_services_v1_role_service_proto_rawDesc = []byte{ 0x65, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x75, - 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x75, - 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x58, 0x0a, 0x19, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, + 0x75, 0x65, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x70, + 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, + 0x73, 0x22, 0x58, 0x0a, 0x19, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, + 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, + 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xc7, 0x01, 0x0a, 0x18, + 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x17, + 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, + 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x24, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, + 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x58, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, - 0xdb, 0x01, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, - 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, - 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, - 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, - 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x12, - 0x1c, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x58, 0x0a, - 0x19, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, - 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, + 0xca, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, + 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x36, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, + 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, + 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x5b, 0x0a, 0x1c, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, + 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb5, 0x01, 0x0a, 0x14, 0x41, 0x64, + 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, + 0x64, 0x12, 0x36, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x61, + 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, + 0x73, 0x22, 0x54, 0x0a, 0x15, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, - 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xde, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x1d, - 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, - 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, - 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x5b, 0x0a, 0x1c, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, + 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb5, 0x01, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x52, + 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, + 0x36, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x22, + 0x54, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb5, 0x01, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, - 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, - 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x36, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x22, 0x54, 0x0a, - 0x15, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, - 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, - 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x22, 0xb5, 0x01, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, - 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, - 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, - 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, - 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x22, 0x54, 0x0a, 0x15, 0x53, - 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, - 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, - 0x6d, 0x22, 0xb8, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, - 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, - 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, - 0x72, 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x05, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x22, 0x57, 0x0a, 0x18, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0xe4, 0x18, 0x0a, 0x0b, 0x52, 0x6f, 0x6c, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xe1, 0x01, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, - 0x65, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, - 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7d, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x60, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, - 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, - 0x5a, 0x3a, 0x12, 0x32, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, - 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, - 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, - 0x65, 0x6d, 0x92, 0x41, 0x14, 0x12, 0x12, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, - 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0xce, 0x01, 0x0a, 0x09, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb8, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, + 0x12, 0x36, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x61, 0x6e, + 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, + 0x22, 0x57, 0x0a, 0x18, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, + 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0xe4, 0x18, 0x0a, 0x0b, 0x52, 0x6f, + 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xe1, 0x01, 0x0a, 0x07, 0x47, 0x65, + 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4a, 0x12, 0x17, 0x2f, 0x76, - 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, - 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x5a, 0x2f, 0x12, 0x2d, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, - 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, - 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x92, 0x41, 0x11, 0x12, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x73, - 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0xf0, 0x01, 0x0a, 0x0a, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x82, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x62, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, - 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x5a, 0x3b, 0x22, 0x2d, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, - 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, - 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x92, 0x41, 0x17, 0x12, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, - 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0xf3, 0x01, - 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x85, 0x01, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x6c, 0x32, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, - 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, - 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x5a, 0x40, 0x32, 0x32, 0x2f, 0x76, 0x31, 0x2f, - 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x92, 0x41, 0x10, 0x12, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x52, - 0x6f, 0x6c, 0x65, 0x12, 0xda, 0x01, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, - 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x6d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x54, 0x2a, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x6f, - 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, - 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x5a, 0x34, 0x2a, 0x32, 0x2f, 0x76, 0x31, 0x2f, 0x6f, - 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, - 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x92, 0x41, 0x10, - 0x12, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, - 0x12, 0xbf, 0x02, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, - 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, - 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, - 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0xbc, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x8e, 0x01, 0x22, 0x30, 0x2f, - 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, - 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, - 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, - 0x01, 0x2a, 0x5a, 0x51, 0x22, 0x46, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, - 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, - 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, - 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, - 0x64, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x62, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x24, 0x12, 0x22, - 0x41, 0x64, 0x64, 0x73, 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, - 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x52, 0x6f, - 0x6c, 0x65, 0x12, 0xfe, 0x02, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, - 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, - 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, - 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xfb, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x8e, 0x01, 0x22, - 0x30, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, - 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, - 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, - 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x51, 0x22, 0x46, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7d, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x60, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, + 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, + 0x7b, 0x69, 0x64, 0x7d, 0x5a, 0x3a, 0x12, 0x32, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, - 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, - 0x73, 0x65, 0x74, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, 0x01, - 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x63, - 0x12, 0x61, 0x53, 0x65, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x2f, - 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x52, - 0x6f, 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, - 0x79, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x20, 0x74, 0x68, 0x61, - 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x2e, 0x12, 0xe2, 0x02, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, - 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x37, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, - 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0xd6, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x94, 0x01, 0x22, 0x33, 0x2f, 0x76, 0x31, 0x2f, 0x6f, - 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, - 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, 0x01, - 0x2a, 0x5a, 0x54, 0x22, 0x49, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, - 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, - 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, - 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, 0x01, - 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x38, - 0x12, 0x36, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, - 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, - 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x12, 0x9e, 0x02, 0x0a, 0x0d, 0x41, 0x64, 0x64, - 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, - 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, - 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0xa7, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x86, 0x01, 0x22, 0x2c, 0x2f, 0x76, 0x31, 0x2f, 0x6f, - 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, - 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, - 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x4d, 0x22, 0x42, 0x2f, 0x76, - 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, - 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, - 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, - 0x41, 0x17, 0x12, 0x15, 0x41, 0x64, 0x64, 0x73, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, - 0x74, 0x6f, 0x20, 0x61, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0xda, 0x02, 0x0a, 0x0d, 0x53, 0x65, - 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x63, 0x6f, + 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x14, 0x12, 0x12, 0x47, 0x65, 0x74, 0x73, 0x20, + 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0xce, 0x01, + 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, - 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x4a, + 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, + 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x5a, 0x2f, 0x12, 0x2d, 0x2f, 0x76, 0x31, + 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x92, 0x41, 0x11, 0x12, 0x0f, 0x4c, + 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0xf0, + 0x01, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, - 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0xe3, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x86, 0x01, 0x22, 0x2c, 0x2f, 0x76, 0x31, 0x2f, - 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, - 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, - 0x74, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x4d, 0x22, 0x42, 0x2f, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x82, 0x01, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x62, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, + 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x3a, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x5a, 0x3b, 0x22, 0x2d, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, + 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, + 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x17, 0x12, 0x15, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c, + 0x65, 0x12, 0xf3, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, + 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x85, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x6c, 0x32, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, + 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, + 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x5a, 0x40, 0x32, 0x32, + 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, + 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x10, 0x12, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, + 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0xda, 0x01, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x54, 0x2a, 0x1c, 0x2f, + 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, + 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x5a, 0x34, 0x2a, 0x32, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, - 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, - 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x92, 0x41, 0x53, 0x12, 0x51, 0x53, 0x65, 0x74, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, - 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x6f, - 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x12, 0xb3, 0x02, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x33, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, - 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, + 0x7d, 0x92, 0x41, 0x10, 0x12, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, + 0x52, 0x6f, 0x6c, 0x65, 0x12, 0xbf, 0x02, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, + 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, + 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, + 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbc, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x8e, + 0x01, 0x22, 0x30, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, + 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, + 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, + 0x61, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x51, 0x22, 0x46, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, + 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, + 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, + 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, + 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, + 0x41, 0x24, 0x12, 0x22, 0x41, 0x64, 0x64, 0x73, 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, 0x61, + 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x74, 0x6f, 0x20, + 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0xfe, 0x02, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x52, 0x6f, + 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x34, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, + 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xfb, 0x01, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x8e, 0x01, 0x22, 0x30, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, + 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, + 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, + 0x69, 0x70, 0x61, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x51, 0x22, 0x46, 0x2f, 0x76, 0x31, 0x2f, + 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, + 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, + 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, + 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x92, 0x41, 0x63, 0x12, 0x61, 0x53, 0x65, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, + 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x74, 0x6f, + 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, + 0x67, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x12, 0xe2, 0x02, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, + 0x12, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb3, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x8c, 0x01, - 0x22, 0x2f, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, + 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, + 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, + 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0xd6, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x94, 0x01, 0x22, 0x33, 0x2f, + 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, + 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, + 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, + 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x54, 0x22, 0x49, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, + 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, + 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, + 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, + 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, + 0x6d, 0x92, 0x41, 0x38, 0x12, 0x36, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x55, 0x73, 0x65, 0x72, + 0x73, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x12, 0x9e, 0x02, 0x0a, + 0x0d, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x30, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, + 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, + 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0xa7, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x86, 0x01, 0x22, 0x2c, 0x2f, + 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, + 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, + 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x4d, + 0x22, 0x42, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, + 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, + 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x67, 0x72, + 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x92, 0x41, 0x17, 0x12, 0x15, 0x41, 0x64, 0x64, 0x73, 0x20, 0x67, 0x72, 0x61, + 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0xda, 0x02, + 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, + 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, + 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe3, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x86, 0x01, 0x22, 0x2c, + 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, + 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, + 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x5a, + 0x4d, 0x22, 0x42, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, + 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, + 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x67, + 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x53, 0x12, 0x51, 0x53, 0x65, 0x74, 0x20, 0x67, 0x72, 0x61, + 0x6e, 0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x2c, 0x20, + 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x67, 0x72, 0x61, + 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x12, 0xb3, 0x02, 0x0a, 0x10, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, + 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb3, 0x01, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x8c, 0x01, 0x22, 0x2f, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, + 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, + 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x67, + 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x50, 0x22, 0x45, 0x2f, 0x76, 0x31, 0x2f, + 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, - 0x73, 0x3a, 0x01, 0x2a, 0x5a, 0x50, 0x22, 0x45, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, - 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, - 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x3a, - 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x3a, 0x01, 0x2a, - 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x1d, 0x12, - 0x1b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, - 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x2e, 0x42, 0x4f, 0x5a, 0x4d, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x67, 0x72, 0x61, + 0x6e, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x2e, + 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, + 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, + 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/iam/repository_principal_role.go b/internal/iam/repository_principal_role.go index 748b66482..3e84ba0ad 100644 --- a/internal/iam/repository_principal_role.go +++ b/internal/iam/repository_principal_role.go @@ -3,6 +3,7 @@ package iam import ( "context" "fmt" + "strings" "github.com/hashicorp/watchtower/internal/db" "github.com/hashicorp/watchtower/internal/oplog" @@ -12,10 +13,14 @@ import ( // groupIds) to a role (roleId). The role's current db version must match the // roleVersion or an error will be returned. The list of current PrincipalRoles // after the adds will be returned on success. -func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, userIds, groupIds []string, opt ...Option) ([]PrincipalRole, error) { +func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, principalIds []string, opt ...Option) ([]PrincipalRole, error) { if roleId == "" { return nil, fmt.Errorf("add principal roles: missing role id: %w", db.ErrInvalidParameter) } + userIds, groupIds, err := splitPrincipals(principalIds) + if err != nil { + return nil, fmt.Errorf("add principal roles: error parsing principals: %w", err) + } if len(userIds) == 0 && len(groupIds) == 0 { return nil, fmt.Errorf("add principal roles: missing either user or groups to add: %w", db.ErrInvalidParameter) } @@ -114,7 +119,7 @@ func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleV // principals as need to reconcile the existing principals with the principals // requested. If both userIds and groupIds are empty, the principal roles will // be cleared. -func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, userIds, groupIds []string, opt ...Option) ([]PrincipalRole, int, error) { +func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, principalIds []string, opt ...Option) ([]PrincipalRole, int, error) { if roleId == "" { return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: missing role id: %w", db.ErrInvalidParameter) } @@ -127,6 +132,10 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV // it's "safe" to do this lookup outside the DoTx transaction because we // have a roleVersion so the principals can’t change without the version // changing. + userIds, groupIds, err := splitPrincipals(principalIds) + if err != nil { + return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: error parsing principals: %w", err) + } toSet, err := r.principalsToSet(ctx, &role, userIds, groupIds) if err != nil { return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: unable to determine set: %w", err) @@ -243,10 +252,14 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV // DeletePrincipalRoles principals (userIds and/or groupIds) from a role // (roleId). The role's current db version must match the roleVersion or an // error will be returned. -func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, userIds, groupIds []string, opt ...Option) (int, error) { +func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, principalIds []string, opt ...Option) (int, error) { if roleId == "" { return db.NoRowsAffected, fmt.Errorf("delete principal roles: missing role id: %w", db.ErrInvalidParameter) } + userIds, groupIds, err := splitPrincipals(principalIds) + if err != nil { + return db.NoRowsAffected, fmt.Errorf("remove principal roles: error parsing principals: %w", err) + } if len(userIds) == 0 && len(groupIds) == 0 { return db.NoRowsAffected, fmt.Errorf("delete principal roles: missing either user or groups to delete: %w", db.ErrInvalidParameter) } @@ -441,3 +454,20 @@ func (r *Repository) principalsToSet(ctx context.Context, role *Role, userIds, g return toSet, nil } + +func splitPrincipals(principals []string) ([]string, []string, error) { + var users, groups []string + for _, principal := range principals { + switch { + case strings.HasPrefix(principal, UserPrefix): + users = append(users, principal) + // TODO: This needs to handle all of the kinds of group prefixes (sg_, dg_, etc.) + case strings.HasPrefix(principal, GroupPrefix): + groups = append(groups, principal) + default: + return nil, nil, fmt.Errorf("invalid principal ID %q: %w", principal, db.ErrInvalidParameter) + } + } + + return users, groups, nil +} diff --git a/internal/iam/repository_principal_role_test.go b/internal/iam/repository_principal_role_test.go index 4e0cd7ed2..715ccc6b9 100644 --- a/internal/iam/repository_principal_role_test.go +++ b/internal/iam/repository_principal_role_test.go @@ -132,7 +132,8 @@ func TestRepository_AddPrincipalRoles(t *testing.T) { groupIds = append(groupIds, g.PublicId) } } - got, err := repo.AddPrincipalRoles(context.Background(), roleId, tt.args.roleVersion, userIds, groupIds, tt.args.opt...) + principalIds := append(userIds, groupIds...) + got, err := repo.AddPrincipalRoles(context.Background(), roleId, tt.args.roleVersion, principalIds, tt.args.opt...) if tt.wantErr { require.Error(err) if tt.wantErrIs != nil { @@ -236,7 +237,7 @@ func TestRepository_ListPrincipalRoles(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - require.NoError(conn.Where("1=1").Delete(allocRole()).Error) + require.NoError(conn.Where("public_id != ?", "r_default").Delete(allocRole()).Error) role := TestRole(t, conn, tt.createScopeId) userRoles := make([]string, 0, tt.createCnt) groupRoles := make([]string, 0, tt.createCnt) @@ -246,7 +247,8 @@ func TestRepository_ListPrincipalRoles(t *testing.T) { g := TestGroup(t, conn, tt.createScopeId) groupRoles = append(groupRoles, g.PublicId) } - testRoles, err := repo.AddPrincipalRoles(context.Background(), role.PublicId, role.Version, userRoles, groupRoles, tt.args.opt...) + principalIds := append(userRoles, groupRoles...) + testRoles, err := repo.AddPrincipalRoles(context.Background(), role.PublicId, role.Version, principalIds, tt.args.opt...) require.NoError(err) assert.Equal(tt.createCnt, len(testRoles)) @@ -406,7 +408,8 @@ func TestRepository_DeletePrincipalRoles(t *testing.T) { groupIds = append(groupIds, g.PublicId) } } - principalRoles, err := repo.AddPrincipalRoles(context.Background(), tt.args.role.PublicId, 1, userIds, groupIds, tt.args.opt...) + principalIds := append(userIds, groupIds...) + principalRoles, err := repo.AddPrincipalRoles(context.Background(), tt.args.role.PublicId, 1, principalIds, tt.args.opt...) require.NoError(err) assert.Equal(tt.args.createUserCnt+tt.args.createGroupCnt, len(principalRoles)) @@ -425,7 +428,8 @@ func TestRepository_DeletePrincipalRoles(t *testing.T) { default: roleId = tt.args.role.PublicId } - deletedRows, err := repo.DeletePrincipalRoles(context.Background(), roleId, 2, deleteUserIds, deleteGroupIds, tt.args.opt...) + principalIds = append(deleteUserIds, deleteGroupIds...) + deletedRows, err := repo.DeletePrincipalRoles(context.Background(), roleId, 2, principalIds, tt.args.opt...) if tt.wantErr { assert.Error(err) assert.Equal(0, deletedRows) @@ -480,7 +484,7 @@ func TestRepository_SetPrincipalRoles(t *testing.T) { users := createUsersFn() grps := createGrpsFn() var err error - _, err = repo.AddPrincipalRoles(context.Background(), role.PublicId, 1, users, grps) + _, err = repo.AddPrincipalRoles(context.Background(), role.PublicId, 1, append(users, grps...)) require.NoError(t, err) return users, grps } @@ -568,8 +572,8 @@ func TestRepository_SetPrincipalRoles(t *testing.T) { if tt.args.addToOrigGrps { tt.args.groupIds = append(tt.args.groupIds, origGrps...) } - - got, affectedRows, err := repo.SetPrincipalRoles(context.Background(), tt.args.role.PublicId, tt.args.roleVersion, tt.args.userIds, tt.args.groupIds, tt.args.opt...) + principalIds := append(tt.args.userIds, tt.args.groupIds...) + got, affectedRows, err := repo.SetPrincipalRoles(context.Background(), tt.args.role.PublicId, tt.args.roleVersion, principalIds, tt.args.opt...) if tt.wantErr { require.Error(err) return @@ -619,7 +623,7 @@ func TestRepository_principalsToSet(t *testing.T) { users := createUsersFn() grps := createGrpsFn() role := TestRole(t, conn, proj.PublicId) - _, err := repo.AddPrincipalRoles(context.Background(), role.PublicId, 1, users, grps) + _, err := repo.AddPrincipalRoles(context.Background(), role.PublicId, 1, append(users, grps...)) require.NoError(t, err) return role, users, grps } diff --git a/internal/iam/repository_role_grant.go b/internal/iam/repository_role_grant.go index 6f45cf4a5..3e1399cc0 100644 --- a/internal/iam/repository_role_grant.go +++ b/internal/iam/repository_role_grant.go @@ -351,3 +351,90 @@ func (r *Repository) ListRoleGrants(ctx context.Context, roleId string, opt ...O } return roleGrants, nil } + +func (r *Repository) GrantsForUser(ctx context.Context, userId string, opt ...Option) ([]perms.GrantPair, error) { + if userId == "" { + return nil, fmt.Errorf("get grants for user: missing user id: %w", db.ErrInvalidParameter) + } + + const ( + anonUser = `where public_id in ($1)` + authUser = `where public_id in ('u_anon', 'u_auth', $1)` + grantsQuery = ` +with +users (id) as ( + select public_id + from iam_user + %s -- anonUser || authUser +), +user_groups (id) as ( + select group_id + from iam_group_member_user, + users + where member_id in (users.id) +), +group_roles (role_id) as ( + select role_id + from iam_group_role, + user_groups + where principal_id in (user_groups.id) +), +user_roles (role_id) as ( + select role_id + from iam_user_role, + users + where principal_id in (users.id) +), +user_group_roles (role_id) as ( + select role_id + from group_roles + union + select role_id + from user_roles +), +roles (role_id, grant_scope_id) as ( + select iam_role.public_id, + iam_role.grant_scope_id + from iam_role, + user_group_roles + where public_id in (user_group_roles.role_id) +), +final (role_scope, role_grant) as ( + select roles.grant_scope_id, + iam_role_grant.canonical_grant + from roles + inner + join iam_role_grant + on roles.role_id = iam_role_grant.role_id +) +select role_scope as scope_id, role_grant as grant from final; + ` + ) + + var query string + switch userId { + case "u_anon": + query = fmt.Sprintf(grantsQuery, anonUser) + default: + query = fmt.Sprintf(grantsQuery, authUser) + } + + var grants []perms.GrantPair + tx, err := r.reader.DB() + if err != nil { + return nil, err + } + rows, err := tx.Query(query, userId) + if err != nil { + return nil, err + } + defer rows.Close() + for rows.Next() { + var g perms.GrantPair + if err := r.reader.ScanRows(rows, &g); err != nil { + return nil, err + } + grants = append(grants, g) + } + return grants, nil +} diff --git a/internal/iam/repository_role_grant_test.go b/internal/iam/repository_role_grant_test.go index 8df0d8167..f60c83e4d 100644 --- a/internal/iam/repository_role_grant_test.go +++ b/internal/iam/repository_role_grant_test.go @@ -184,7 +184,7 @@ func TestRepository_ListRoleGrants(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - require.NoError(conn.Where("1=1").Delete(allocRole()).Error) + require.NoError(conn.Where("public_id != ?", "r_default").Delete(allocRole()).Error) role := TestRole(t, conn, tt.createScopeId) roleGrants := make([]string, 0, tt.createCnt) for i := 0; i < tt.createCnt; i++ { diff --git a/internal/iam/repository_scope.go b/internal/iam/repository_scope.go index c418fc3bb..d402980d6 100644 --- a/internal/iam/repository_scope.go +++ b/internal/iam/repository_scope.go @@ -30,7 +30,7 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, opt ...Option) ( } opts := getOpts(opt...) var publicId string - t := scope.StringToScopeType(s.Type) + t := scope.Map[s.Type] if opts.withPublicId != "" { if !strings.HasPrefix(opts.withPublicId, t.Prefix()+"_") { return nil, fmt.Errorf("create scope: passed-in public ID %q has wrong prefix for type %q which uses prefix %q", opts.withPublicId, t.String(), t.Prefix()) diff --git a/internal/perms/grants.go b/internal/perms/grants.go index 0d95be980..bfbcbfd07 100644 --- a/internal/perms/grants.go +++ b/internal/perms/grants.go @@ -12,6 +12,13 @@ import ( "github.com/hashicorp/watchtower/internal/types/scope" ) +// GrantPair is simply a struct that can be reference from other code to return +// a set of scopes and grants to parse +type GrantPair struct { + ScopeId string + Grant string +} + // Scope provides an in-memory representation of iam.Scope without the // underlying storage references or capabilities. type Scope struct { @@ -123,7 +130,10 @@ func (g *Grant) unmarshalJSON(data []byte) error { if !ok { return fmt.Errorf("unable to interpret %q as string", "type") } - g.typ = resource.StringToResourceType(typ) + g.typ = resource.Map[typ] + if g.typ == resource.Unknown { + return fmt.Errorf("unknown type specifier %q", typ) + } } if rawActions, ok := raw["actions"]; ok { interfaceActions, ok := rawActions.([]interface{}) @@ -169,7 +179,7 @@ func (g *Grant) unmarshalText(grantString string) error { case "type": typeString := strings.ToLower(kv[1]) - g.typ = resource.StringToResourceType(typeString) + g.typ = resource.Map[typeString] if g.typ == resource.Unknown { return fmt.Errorf("unknown type specifier %q", typeString) } @@ -273,6 +283,7 @@ func (g Grant) validateType() error { resource.HostCatalog, resource.HostSet, resource.Host, + resource.Org, resource.Target: return nil } diff --git a/internal/proto/local/controller/api/resources/roles/v1/role.proto b/internal/proto/local/controller/api/resources/roles/v1/role.proto index 552e03a75..5017693ec 100644 --- a/internal/proto/local/controller/api/resources/roles/v1/role.proto +++ b/internal/proto/local/controller/api/resources/roles/v1/role.proto @@ -7,6 +7,26 @@ option go_package = "github.com/hashicorp/watchtower/internal/gen/controller/api import "google/protobuf/timestamp.proto"; import "google/protobuf/wrappers.proto"; +message Principal { + // The ID of the principal. + // Output only. + string id = 1; + + // The type of the principal. + // Output only. + string type = 2; + + // The scope ID of the principal. + // Output only. + string scope_id = 3 [json_name="scope_id"]; +} + +message Grant { + // The user-supplied string. + // Output only. + +} + // Role contains all fields related to a Role resource message Role { // The ID of the Role @@ -42,13 +62,15 @@ message Role { // Output only. uint32 version = 8; - // The principals that are assigned this role. + // The IDs (only) of principals that are assigned to this role. // Output only. - repeated string user_ids = 9; - // Output only. - repeated string group_ids = 10; + repeated string principal_ids = 9 [json_name="principal_ids"]; - // The grants that this role provides for it's principals. + // The principals that are assigned to this role. + // Output only. + repeated Principal principals = 10; + + // The grants that this role provides for its principals. // Output only. repeated string grants = 11; diff --git a/internal/proto/local/controller/api/services/v1/authenticate_service.proto b/internal/proto/local/controller/api/services/v1/authenticate_service.proto new file mode 100644 index 000000000..5f54640b9 --- /dev/null +++ b/internal/proto/local/controller/api/services/v1/authenticate_service.proto @@ -0,0 +1,63 @@ +syntax = "proto3"; + +package controller.api.services.v1; + +option go_package = "github.com/hashicorp/watchtower/internal/gen/controller/api/services;services"; + +import "protoc-gen-swagger/options/annotations.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/struct.proto"; +import "controller/api/resources/authtokens/v1/authtoken.proto"; + +service AuthenticationService { + // Authenticate validates credentials provided and returns an auth token. + rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse) { + option (google.api.http) = { + post: "/v1/orgs/{org_id}/auth-methods/{auth_method_id}:authenticate" + body: "*" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { + summary: "Authenticate a user to an Org and retrieve an authentication token." + }; + } + + // Logout terminates a user's current session. + rpc Deauthenticate(DeauthenticateRequest) returns (DeauthenticateResponse) { + option (google.api.http) = { + post: "/v1/orgs/{org_id}:deauthenticate" + }; + option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { + summary: "Deauthenticate an authentication token from an Org." + }; + } +} + +// The layout of the struct for "credentials" field in AuthenticateRequest. This message isn't +// directly referenced anywhere but is used here to define the expected field names and types. +message PasswordCredentials { + string name = 1; + string password = 2; +} + +message AuthenticateRequest { + string org_id = 1; + // The id to the authmethod in the system being used for authentication. The auth method must be in the org + // being logged in to. + string auth_method_id = 2; + // This can be "cookie" or "token". If not provided, "token" will be used. For now only type "token" is returned. + string token_type = 3; + + // credentials are the different possible credential names depending on what type of auth method is used. + // For password auth method: should include only "name" and "password". + google.protobuf.Struct credentials = 8; +} + +message AuthenticateResponse { + resources.authtokens.v1.AuthToken item = 1; +} + +message DeauthenticateRequest { + string org_id = 1; +} +message DeauthenticateResponse {} \ No newline at end of file diff --git a/internal/proto/local/controller/api/services/v1/org_service.proto b/internal/proto/local/controller/api/services/v1/org_service.proto index 80ff026e4..265339b9c 100644 --- a/internal/proto/local/controller/api/services/v1/org_service.proto +++ b/internal/proto/local/controller/api/services/v1/org_service.proto @@ -7,7 +7,6 @@ option go_package = "github.com/hashicorp/watchtower/internal/gen/controller/api import "protoc-gen-swagger/options/annotations.proto"; import "google/api/annotations.proto"; import "controller/api/resources/scopes/v1/org.proto"; -import "controller/api/resources/authtokens/v1/authtoken.proto"; service OrgService { @@ -33,28 +32,6 @@ service OrgService { summary: "Lists all Orgs" }; } - - // Authenticate validates credentials provided and returns an auth token. - rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse) { - option (google.api.http) = { - post: "/v1/orgs/{org_id}:authenticate" - body: "*" - response_body: "item" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - summary: "Authenticate a user to an Org and retrieve an authentication token." - }; - } - - // Logout terminates a user's current session. - rpc Deauthenticate(DeauthenticateRequest) returns (DeauthenticateResponse) { - option (google.api.http) = { - post: "/v1/orgs/{org_id}:deauthenticate" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - summary: "Deauthenticate an authentication token from an Org." - }; - } } message GetOrgRequest { @@ -73,28 +50,3 @@ message ListOrgsRequest { message ListOrgsResponse { repeated resources.scopes.v1.Org items = 1; } - -message PasswordCredential { - string login_name = 1; - string password = 2; -} - -message AuthenticateRequest { - string org_id = 1; - // The id to the authmethod in the system being used for authentication. The auth method must be in the org - // being logged in to. - string auth_method_id = 2; - // This can be "cookie" or "token". If not provided, "token" will be used. For now only type "token" is returned. - string token_type = 3; - - PasswordCredential password_credential = 4; -} - -message AuthenticateResponse { - resources.authtokens.v1.AuthToken item = 1; -} - -message DeauthenticateRequest { - string org_id = 1; -} -message DeauthenticateResponse {} \ No newline at end of file diff --git a/internal/proto/local/controller/api/services/v1/role_service.proto b/internal/proto/local/controller/api/services/v1/role_service.proto index c2cbd10f7..52d65faae 100644 --- a/internal/proto/local/controller/api/services/v1/role_service.proto +++ b/internal/proto/local/controller/api/services/v1/role_service.proto @@ -296,8 +296,7 @@ message AddRolePrincipalsRequest { // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. google.protobuf.UInt32Value version = 4; - repeated string user_ids = 5 [json_name="user_ids"]; - repeated string group_ids = 6 [json_name="group_ids"]; + repeated string principal_ids = 5 [json_name="principal_ids"]; } message AddRolePrincipalsResponse { @@ -311,8 +310,7 @@ message SetRolePrincipalsRequest { // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. google.protobuf.UInt32Value version = 4; - repeated string user_ids = 5 [json_name="user_ids"]; - repeated string group_ids = 6 [json_name="group_ids"]; + repeated string principal_ids = 5 [json_name="principal_ids"]; } message SetRolePrincipalsResponse { @@ -326,8 +324,7 @@ message RemoveRolePrincipalsRequest { // The version ensures the role hasn't changed since it was last retrieved and if // it has the request will fail. google.protobuf.UInt32Value version = 4; - repeated string user_ids = 5 [json_name="user_ids"]; - repeated string group_ids = 6 [json_name="group_ids"]; + repeated string principal_ids = 5 [json_name="principal_ids"]; } message RemoveRolePrincipalsResponse { diff --git a/internal/servers/controller/config.go b/internal/servers/controller/config.go index 1a7f4823a..fb59d757b 100644 --- a/internal/servers/controller/config.go +++ b/internal/servers/controller/config.go @@ -1,14 +1,17 @@ package controller import ( - "context" - "github.com/hashicorp/watchtower/internal/cmd/base" "github.com/hashicorp/watchtower/internal/cmd/config" ) type Config struct { + // The base Server object, containing things shared between Controllers and + // Workers *base.Server - RawConfig *config.Config - BaseContext context.Context + // The underlying configuration, passed in here to avoid duplicating values + // everywhere + RawConfig *config.Config + // If set, authorization checking occurrs but failures are ignored + DisableAuthorizationFailures bool } diff --git a/internal/servers/controller/cors_test.go b/internal/servers/controller/cors_test.go index d7f18e7fc..e1fa67857 100644 --- a/internal/servers/controller/cors_test.go +++ b/internal/servers/controller/cors_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/hashicorp/watchtower/internal/cmd/config" ) @@ -65,8 +66,9 @@ func TestHandler_CORS(t *testing.T) { t.Fatal(err) } tc := NewTestController(t, &TestControllerOpts{ - Config: cfg, - DefaultOrgId: "o_1234567890", + Config: cfg, + DefaultOrgId: "o_1234567890", + DisableAuthorizationFailures: true, }) defer tc.Shutdown() @@ -161,12 +163,13 @@ func TestHandler_CORS(t *testing.T) { t.Run(c.name, func(t *testing.T) { // Create a client with the right address client := tc.Client() - client.SetAddr(tc.ApiAddrs()[c.listenerNum-1]) + err := client.SetAddr(tc.ApiAddrs()[c.listenerNum-1]) + require.NoError(t, err) client.SetOrg("o_1234567890") // Create the request req, err := client.NewRequest(tc.Context(), c.method, "projects", nil) - assert.NoError(t, err) + require.NoError(t, err) // Append headers if c.origin != "" { @@ -178,7 +181,7 @@ func TestHandler_CORS(t *testing.T) { // Run the request, do basic checks resp, err := client.Do(req) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, c.code, resp.HttpResponse().StatusCode) // If options and we expect it to be successful, run some checks diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index c01bebeaf..9172fd5c6 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -3,9 +3,10 @@ package controller import ( "context" "encoding/json" + "errors" "fmt" "net/http" - "path" + "os" "strings" "time" @@ -15,7 +16,9 @@ import ( "github.com/hashicorp/watchtower/api" "github.com/hashicorp/watchtower/globals" "github.com/hashicorp/watchtower/internal/gen/controller/api/services" + "github.com/hashicorp/watchtower/internal/perms" "github.com/hashicorp/watchtower/internal/servers/controller/handlers" + "github.com/hashicorp/watchtower/internal/servers/controller/handlers/authenticate" "github.com/hashicorp/watchtower/internal/servers/controller/handlers/groups" "github.com/hashicorp/watchtower/internal/servers/controller/handlers/host_catalogs" "github.com/hashicorp/watchtower/internal/servers/controller/handlers/host_sets" @@ -24,6 +27,10 @@ import ( "github.com/hashicorp/watchtower/internal/servers/controller/handlers/projects" "github.com/hashicorp/watchtower/internal/servers/controller/handlers/roles" "github.com/hashicorp/watchtower/internal/servers/controller/handlers/users" + "github.com/hashicorp/watchtower/internal/types/action" + "github.com/hashicorp/watchtower/internal/types/resource" + "github.com/hashicorp/watchtower/internal/types/scope" + "github.com/kr/pretty" ) type HandlerProperties struct { @@ -54,8 +61,7 @@ func handleGrpcGateway(c *Controller) (http.Handler, error) { // Register*ServiceHandlerServer methods ignore the passed in ctx. Using the baseContext now just in case this changes // in the future, at which point we'll want to be using the baseContext. ctx := c.baseContext - mux := runtime.NewServeMux(runtime.WithMetadata(handlers.TokenAuthenticator(c.logger, c.AuthTokenRepoFn)), - runtime.WithProtoErrorHandler(handlers.ErrorHandler(c.logger))) + mux := runtime.NewServeMux(runtime.WithProtoErrorHandler(handlers.ErrorHandler(c.logger))) hcs, err := host_catalogs.NewService(c.StaticHostRepoFn) if err != nil { return nil, fmt.Errorf("failed to create host catalog handler service: %w", err) @@ -69,6 +75,13 @@ func handleGrpcGateway(c *Controller) (http.Handler, error) { if err := services.RegisterHostServiceHandlerServer(ctx, mux, &hosts.Service{}); err != nil { return nil, fmt.Errorf("failed to register host service handler: %w", err) } + auths, err := authenticate.NewService(c.IamRepoFn, c.AuthTokenRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create authentication handler service: %w", err) + } + if err := services.RegisterAuthenticationServiceHandlerServer(ctx, mux, auths); err != nil { + return nil, fmt.Errorf("failed to register authenticate service handler: %w", err) + } os, err := orgs.NewService(c.IamRepoFn) if err != nil { return nil, fmt.Errorf("failed to create org handler service: %w", err) @@ -121,27 +134,76 @@ func wrapHandlerWithCommonFuncs(h http.Handler, c *Controller, props HandlerProp if maxRequestSize == 0 { maxRequestSize = globals.DefaultMaxRequestSize } - var defaultOrgId string - if c != nil && c.conf != nil { - defaultOrgId = c.conf.DefaultOrgId + + logUrls := os.Getenv("WATCHTOWER_LOG_URLS") != "" + + disableAuthzFailures := c.conf.DisableAuthorizationFailures || + (c.conf.RawConfig.DevController && os.Getenv("WATCHTOWER_DEV_SKIP_AUTHZ") != "") + if disableAuthzFailures { + c.logger.Warn("AUTHORIZATION CHECKING DISABLED") } + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if defaultOrgId != "" { - splitPath := strings.Split(r.URL.Path, "/") - if len(splitPath) >= 3 && splitPath[2] == "projects" { - http.Redirect(w, r, path.Join("/v1/orgs", defaultOrgId, strings.Join(splitPath[2:], "/")), 307) - return - } + if logUrls { + c.logger.Trace("request received", "url", r.URL.String()) } // Set the Cache-Control header for all responses returned w.Header().Set("Cache-Control", "no-store") - // Start with the request context - ctx := r.Context() - var cancelFunc context.CancelFunc - // Add our timeout - ctx, cancelFunc = context.WithTimeout(ctx, maxRequestDuration) + // Start with the request context and our timeout + ctx, cancelFunc := context.WithTimeout(r.Context(), maxRequestDuration) + defer cancelFunc() + + userId := "u_anon" + var res *perms.Resource + var act action.Type + var err error + var authzResults *perms.ACLResults + + if r.Method == http.MethodOptions { + // Don't perform authorization checking for preflight requests, at + // least for now. We could add it later if we wanted as an action on + // global or something but likely it would just be enabled/disabled + // per listener instead. + goto AUTHZ_FINISHED + } + + // Perform authz checking + res, act, err = decorateAuthParams(r) + if err != nil { + c.logger.Trace("error reading auth parameters from URL", "url", r.URL.Path, "error", err) + // Maybe this isn't the best option, but a URL we can't parse from + // an auth perspective is probably just an invalid URL altogether. + // Treating it as a bad request can be perceived to be less leaky + // than a 404. + w.WriteHeader(http.StatusBadRequest) + return + } + + // The resource is only nil with no error if the path is something + // unauthenticated, e.g. the UI serving path. + if res != nil { + authzResults, userId, err = c.performAuthzCheck(ctx, r, res, act) + if err != nil { + c.logger.Error("error during authz check", "error", err) + w.WriteHeader(http.StatusForbidden) + return + } + if userId == "" || !authzResults.Allowed { + // TODO: Decide whether to remove this + if disableAuthzFailures { + c.logger.Info("failed authz info for request", "resource", pretty.Sprint(res), "user_id", userId, "action", act.String()) + } else { + w.WriteHeader(http.StatusForbidden) + return + } + } + } + + AUTHZ_FINISHED: + // Add the user ID to the context + ctx = context.WithValue(ctx, globals.ContextUserIdValue, userId) // Add a size limiter if desired if maxRequestSize > 0 { ctx = context.WithValue(ctx, "max_request_size", maxRequestSize) @@ -150,8 +212,6 @@ func wrapHandlerWithCommonFuncs(h http.Handler, c *Controller, props HandlerProp r = r.WithContext(ctx) h.ServeHTTP(w, r) - cancelFunc() - return }) } @@ -203,8 +263,8 @@ func wrapHandlerWithCors(h http.Handler, props HandlerProperties) http.Handler { w.WriteHeader(http.StatusForbidden) err := &api.Error{ - Status: api.Int(http.StatusForbidden), - Code: api.String("origin forbidden"), + Status: http.StatusForbidden, + Code: "origin forbidden", } enc := json.NewEncoder(w) @@ -231,10 +291,277 @@ func wrapHandlerWithCors(h http.Handler, props HandlerProperties) http.Handler { } h.ServeHTTP(w, req) - return }) } +func decorateAuthParams(r *http.Request) (*perms.Resource, action.Type, error) { + if r == nil { + return nil, action.Unknown, errors.New("decorate auth params: incoming request is nil") + } + + // Remove trailing and leading slashes + trimmedPath := strings.Trim(r.URL.Path, "/") + if !strings.HasPrefix(trimmedPath, "v1") { + // Don't look for auth params for requests to fetch the UI + return nil, action.Unknown, nil + } + splitPath := strings.Split(strings.TrimPrefix(trimmedPath, "v1"), "/") + splitLen := len(splitPath) + if splitLen == 0 { + return nil, action.Unknown, fmt.Errorf("decorate auth params: invalid path") + } + + var act action.Type + var typStr string + scp := scope.Global + res := &perms.Resource{ + ScopeId: scope.Global.String(), + } + + // Handle non-custom types. We'll deal with custom types, including list, + // after parsing the path. + switch r.Method { + case "GET": + act = action.Read + case "POST": + act = action.Create + case "PATCH": + act = action.Update + case "DELETE": + act = action.Delete + default: + return nil, action.Unknown, fmt.Errorf("decorate auth params: unknown method %q", r.Method) + } + + // Look for a custom action + colonSplit := strings.Split(splitPath[splitLen-1], ":") + switch len(colonSplit) { + case 1: + // No custom action specified + case 2: + actStr := colonSplit[len(colonSplit)-1] + act = action.Map[actStr] + if act == action.Unknown || act == action.All { + return nil, action.Unknown, fmt.Errorf("decorate auth params: unknown action %q", actStr) + } + // Keep going with the logic without the custom action + splitPath[splitLen-1] = colonSplit[0] + default: + return nil, action.Unknown, fmt.Errorf("decorate auth params: unexpected number of colons in last segment %q", colonSplit[len(colonSplit)-1]) + } + + // Walk backwards. As we walk backwards we look for scopes and figure out if + // we're operating on a resource or a collection. We also populate the pin. + // The rules for the pin are as follows: + // + // * If the last segment is a collection, the pin is the immediately + // preceding ID + // + // * If the last segment is an ID, the pin is the immediately preceding ID + // not including the last segment + // + // * If at the end of the logic the pin is the id of a scope ("global", + // "o_...", "p_...") then there is no pin. The scopes are already enclosing + // so a pin is redundant. + nextIdIsPin := true + for i := splitLen - 1; i >= 0; i-- { + segment := splitPath[i] + + // Collections don't contain underscores; every resource ID does. + segmentIsCollection := !strings.Contains(segment, "_") + + if !segmentIsCollection && i != splitLen-1 && nextIdIsPin { + res.Pin = segment + nextIdIsPin = false + } + + // Update the scope. Set it to org only if it's at global (that way we + // don't override project with org). We have to check if it's one less + // than the length of the split because operating on the id of a scope + // is actually in the enclosing scope (since you're in the parent scope + // operating on a child scope). + switch segment { + case "projects": + if i < splitLen-2 { + scp = scope.Project + res.ScopeId = splitPath[i+1] + } + case "orgs": + if scp == scope.Global { + if i < splitLen-2 { + scp = scope.Org + res.ScopeId = splitPath[i+1] + } + } + } + + if segment == "" { + // This could be the case if we have an action like + // /orgs/o_1234/projects/p_1234/:set-defaults to act on the project + // itself but within its own scope + continue + } + + if typStr == "" { + // The resource check takes place inside the type check because if + // we've identified the type we have either already identified the + // right-most resource ID or we're operating on a collection, so + // this prevents us from finding a different ID earlier in the path. + // + // We continue on with the enclosing loop anyways though to ensure + // we find the right scope. + if res.Id == "" && !segmentIsCollection { + res.Id = segment + } else { + // Every collection is the plural of the resource type so drop + // the last 's' + if !strings.HasSuffix(segment, "s") { + return nil, action.Unknown, fmt.Errorf("decorate auth params: invalid collection syntax for %q", segment) + } + typStr = strings.TrimSuffix(segment, "s") + } + } + } + + if typStr != "" { + res.Type = resource.Map[typStr] + if res.Type == resource.Unknown { + return nil, action.Unknown, fmt.Errorf("decorate auth params: unknown resource type %q", typStr) + } + } else if res.Id == "" { + return nil, action.Unknown, errors.New("decorate auth params: id and type both not found") + } + + // If we're operating on a collection (that is, the ID is blank) and it's a + // GET, it's actually a list + if res.Id == "" && act == action.Read { + act = action.List + } + + // If the pin ended up being a scope, nil it out + if res.Pin != "" { + if res.Pin == "global" || + strings.HasPrefix(res.Pin, "o_") || + strings.HasPrefix(res.Pin, "p_") { + res.Pin = "" + } + } + + return res, act, nil +} + +type tokenFormat int + +const ( + authTokenTypeUnknown tokenFormat = iota + authTokenTypeBearer + authTokenTypeSplitCookie +) + +const ( + headerAuthMethod = "Authorization" + httpOnlyCookieName = "wt-http-token-cookie" + jsVisibleCookieName = "wt-js-token-cookie" +) + +func (c *Controller) performAuthzCheck(ctx context.Context, req *http.Request, res *perms.Resource, act action.Type) (*perms.ACLResults, string, error) { + userId := "u_anon" + + if res == nil { + return nil, userId, errors.New("perform authz check: res is nil") + } + var receivedTokenType tokenFormat + var fullToken, token, publicId string + + // First, get the token, either from the authorization header or from split + // cookies + { + if authHeader := req.Header.Get("Authorization"); authHeader != "" { + headerSplit := strings.SplitN(strings.TrimSpace(authHeader), " ", 2) + if len(headerSplit) == 2 && strings.EqualFold(strings.TrimSpace(headerSplit[0]), "bearer") { + receivedTokenType = authTokenTypeBearer + fullToken = strings.TrimSpace(headerSplit[1]) + } + } + if receivedTokenType != authTokenTypeBearer { + var httpCookiePayload string + var jsCookiePayload string + if hc, err := req.Cookie(httpOnlyCookieName); err == nil { + httpCookiePayload = hc.Value + } + if jc, err := req.Cookie(jsVisibleCookieName); err == nil { + jsCookiePayload = jc.Value + } + if httpCookiePayload != "" && jsCookiePayload != "" { + receivedTokenType = authTokenTypeSplitCookie + fullToken = jsCookiePayload + httpCookiePayload + } + } + + if receivedTokenType == authTokenTypeUnknown || fullToken == "" { + // We didn't find auth info or a client screwed up and put in a + // blank header instead of nothing at all, so carry on as the + // anonymous user + goto GRANTSLOOKUP + } + + splitFullToken := strings.Split(fullToken, "_") + if len(splitFullToken) != 3 { + return nil, userId, fmt.Errorf("perform authz check: unexpected number of segments in token, expected %d, found %d", 3, len(splitFullToken)) + } + + token = splitFullToken[2] + publicId = strings.Join(splitFullToken[0:2], "_") + + if receivedTokenType == authTokenTypeUnknown || token == "" || publicId == "" { + return nil, userId, fmt.Errorf("perform authz check: after parsing, could not find valid token") + } + } + + // Validate the token and fetch the corresponding user ID + { + tokenRepo, err := c.AuthTokenRepoFn() + if err != nil { + return nil, userId, fmt.Errorf("perform authz check: failed to get authtoken repo: %w", err) + } + + at, err := tokenRepo.ValidateToken(ctx, publicId, token) + if err != nil { + return nil, userId, fmt.Errorf("perform authz check: failed to validate token: %w", err) + } + if at != nil { + userId = at.GetIamUserId() + } + } + +GRANTSLOOKUP: + var parsedGrants []perms.Grant + var grantPairs []perms.GrantPair + // Fetch and parse grants for this user ID + { + iamRepo, err := c.IamRepoFn() + if err != nil { + return nil, userId, fmt.Errorf("perform authz check: failed to get iam repo: %w", err) + } + grantPairs, err = iamRepo.GrantsForUser(ctx, userId) + if err != nil { + return nil, userId, fmt.Errorf("perform authz check: failed to query for user grants: %w", err) + } + parsedGrants = make([]perms.Grant, 0, len(grantPairs)) + for _, pair := range grantPairs { + parsed, err := perms.Parse(pair.ScopeId, userId, pair.Grant) + if err != nil { + return nil, userId, fmt.Errorf("perform authz check: failed to parse grant %#v: %w", pair.Grant, err) + } + parsedGrants = append(parsedGrants, parsed) + } + } + + acl := perms.NewACL(parsedGrants...) + allowed := acl.Allowed(*res, act) + return &allowed, userId, nil +} + /* func WrapForwardedForHandler(h http.Handler, authorizedAddrs []*sockaddr.SockAddrMarshaler, rejectNotPresent, rejectNonAuthz bool, hopSkips int) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/servers/controller/handler_test.go b/internal/servers/controller/handler_test.go index 95d18bc1a..e0b59278e 100644 --- a/internal/servers/controller/handler_test.go +++ b/internal/servers/controller/handler_test.go @@ -2,13 +2,18 @@ package controller import ( "context" + "encoding/json" "fmt" + "io/ioutil" "net/http" "net/http/httptest" + "strings" "testing" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/hashicorp/watchtower/internal/gen/controller/api/services" + "github.com/hashicorp/watchtower/internal/types/action" + "github.com/hashicorp/watchtower/internal/types/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -119,6 +124,28 @@ func TestGrpcGatewayRouting(t *testing.T) { } } +func TestAuthenticationHandler(t *testing.T) { + c := NewTestController(t, &TestControllerOpts{DefaultOrgId: "o_1234567890"}) + defer c.Shutdown() + + resp, err := http.Post(fmt.Sprintf("%s/v1/orgs/o_1234567890/auth-methods/am_1234567890:authenticate", c.ApiAddrs()[0]), "application/json", + strings.NewReader(`{"token_type": null, "credentials": {"name":"test", "password": "test"}}`)) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode, "Got response: %v", resp) + + b, err := ioutil.ReadAll(resp.Body) + require.NoError(t, err) + body := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(b, &body)) + + require.Contains(t, body, "id") + require.Contains(t, body, "token") + pubId, tok := body["id"].(string), body["token"].(string) + assert.NotEmpty(t, pubId) + assert.NotEmpty(t, tok) + assert.Truef(t, strings.HasPrefix(tok, pubId), "Token: %q, Id: %q", tok, pubId) +} + func TestGrpcGatewayRouting_CustomActions(t *testing.T) { ctx := context.Background() // The unimplemented result indicates the grpc routing is happening successfully otherwise it would return NotFound. @@ -167,7 +194,9 @@ func TestGrpcGatewayRouting_CustomActions(t *testing.T) { } func TestHandleGrpcGateway(t *testing.T) { - c := NewTestController(t, nil) + c := NewTestController(t, &TestControllerOpts{ + DisableAuthorizationFailures: true, + }) defer c.Shutdown() cases := []struct { @@ -177,12 +206,12 @@ func TestHandleGrpcGateway(t *testing.T) { }{ { "Non existent path", - "v1/this-is-made-up", - http.StatusNotFound, + "v1/this-is-made-ups", + http.StatusBadRequest, }, { "Unimplemented path", - "v1/orgs/1/projects/2/host-catalogs/3/host-sets/4", + "v1/orgs/1/projects/2/host-catalogs/3/host-sets/hs_4", http.StatusMethodNotAllowed, }, } @@ -200,3 +229,221 @@ func TestHandleGrpcGateway(t *testing.T) { }) } } + +func TestHandler_AuthDecoration(t *testing.T) { + cases := []struct { + name string + path string + method string + action action.Type + scope string + resource resource.Type + id string + pin string + wantErrContains string + }{ + { + name: "global scope, read, with id", + path: "/v1/users/u_anon", + method: "GET", + action: action.Read, + scope: "global", + resource: resource.User, + id: "u_anon", + }, + { + name: "global scope, update, with id", + path: "/v1/auth-methods/am_1234", + method: "PATCH", + action: action.Update, + scope: "global", + resource: resource.AuthMethod, + id: "am_1234", + }, + { + name: "global scope, delete", + path: "/v1/orgs/o_1234", + method: "DELETE", + action: action.Delete, + scope: "global", + resource: resource.Org, + id: "o_1234", + }, + { + name: "global scope, create", + path: "/v1/roles", + method: "POST", + action: action.Create, + scope: "global", + resource: resource.Role, + }, + { + name: "global, invalid collection syntax", + path: "/v1/org", + wantErrContains: "invalid collection syntax", + }, + { + name: "global, custom action", + path: "/v1/orgs/o_123/auth-methods/am_1234:authenticate", + method: "POST", + action: action.Authenticate, + scope: "o_123", + resource: resource.AuthMethod, + id: "am_1234", + }, + { + name: "root, unknown action", + path: "/v1/:authentifake", + wantErrContains: "unknown action", + }, + { + name: "root, unknown empty action", + path: "/v1/:", + wantErrContains: "unknown action", + }, + { + name: "root, invalid method", + path: "/v1/:authenticate", + method: "FOOBAR", + wantErrContains: "unknown method", + }, + { + name: "root, wrong number of colons", + path: "/v1/:auth:enticate", + wantErrContains: "unexpected number of colons", + }, + { + name: "org scope, valid", + path: "/v1/orgs/o_abc123/auth-methods", + method: "POST", + action: action.Create, + scope: "o_abc123", + resource: resource.AuthMethod, + }, + { + name: "project scope, valid", + path: "/v1/orgs/o_abc123/projects/p_1234/host-catalogs", + method: "POST", + action: action.Create, + scope: "p_1234", + resource: resource.HostCatalog, + }, + { + name: "project scope, action on project", + path: "/v1/orgs/o_abc123/projects/p_1234/:set-principals", + method: "POST", + action: action.SetPrincipals, + scope: "p_1234", + resource: resource.Project, + id: "p_1234", + }, + { + name: "org scope, action on project", + path: "/v1/orgs/o_abc123/projects/p_1234:set-principals", + method: "POST", + action: action.SetPrincipals, + scope: "o_abc123", + resource: resource.Project, + id: "p_1234", + }, + { + name: "org scope, get on collection is list", + path: "/v1/orgs/o_abc123/projects", + action: action.List, + scope: "o_abc123", + resource: resource.Project, + }, + { + name: "org scope, action on org", + path: "/v1/orgs/o_abc123/:deauthenticate", + action: action.Deauthenticate, + scope: "o_abc123", + resource: resource.Org, + id: "o_abc123", + }, + { + name: "top level action, invalid", + path: "/v1/:read", + wantErrContains: "id and type both not found", + }, + { + name: "top level, invalid", + path: "/v1/", + wantErrContains: "id and type both not found", + }, + { + name: "non-api path", + path: "/", + }, + { + name: "project scope, pinning collection", + path: "/v1/orgs/o_abc123/projects/p_1234/host-catalogs/hc_1234/host-sets", + action: action.List, + scope: "p_1234", + pin: "hc_1234", + resource: resource.HostSet, + }, + { + name: "project scope, pinning collection, custom action", + path: "/v1/orgs/o_abc123/projects/p_1234/host-catalogs/hc_1234/host-sets:create", + action: action.Create, + scope: "p_1234", + pin: "hc_1234", + resource: resource.HostSet, + }, + { + name: "project scope, pinning id", + path: "/v1/orgs/o_abc123/projects/p_1234/host-catalogs/hc_1234/host-sets/hs_abc", + action: action.Read, + id: "hs_abc", + scope: "p_1234", + pin: "hc_1234", + resource: resource.HostSet, + }, + { + name: "project scope, pinning id, custom action", + path: "/v1/orgs/o_abc123/projects/p_1234/host-catalogs/hc_1234/host-sets/hs_abc:update", + action: action.Update, + id: "hs_abc", + scope: "p_1234", + pin: "hc_1234", + resource: resource.HostSet, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + require, assert := require.New(t), assert.New(t) + if tc.method == "" { + tc.method = "GET" + } + req, err := http.NewRequest(tc.method, fmt.Sprintf("http://127.0.0.1:9200:%s", tc.path), nil) + require.NoError(err) + + res, act, err := decorateAuthParams(nil) + assert.Nil(res) + require.Error(err) + assert.Equal(action.Unknown, act) + assert.Contains(err.Error(), "incoming request is nil") + + res, act, err = decorateAuthParams(req) + if tc.wantErrContains != "" { + require.Error(err) + assert.Contains(err.Error(), tc.wantErrContains, err.Error()) + return + } + require.NoError(err) + + if tc.path == "/" { + return + } + + require.NotNil(res) + require.NotEqual(action.Unknown, act) + assert.Equal(tc.scope, res.ScopeId, "scope") + assert.Equal(tc.action, act, "action") + assert.Equal(tc.resource, res.Type, "type") + assert.Equal(tc.id, res.Id, "id") + assert.Equal(tc.pin, res.Pin, "pin") + }) + } +} diff --git a/internal/servers/controller/handlers/authenticate/authenticate_service.go b/internal/servers/controller/handlers/authenticate/authenticate_service.go new file mode 100644 index 000000000..a9a060986 --- /dev/null +++ b/internal/servers/controller/handlers/authenticate/authenticate_service.go @@ -0,0 +1,164 @@ +package authenticate + +import ( + "context" + "crypto/sha256" + "encoding/base32" + "fmt" + "regexp" + "strings" + "sync/atomic" + + "github.com/hashicorp/watchtower/internal/authtoken" + "github.com/hashicorp/watchtower/internal/db" + pba "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/authtokens" + pbs "github.com/hashicorp/watchtower/internal/gen/controller/api/services" + "github.com/hashicorp/watchtower/internal/iam" + iamStore "github.com/hashicorp/watchtower/internal/iam/store" + "github.com/hashicorp/watchtower/internal/servers/controller/common" + "github.com/hashicorp/watchtower/internal/servers/controller/handlers" + "github.com/hashicorp/watchtower/internal/types/scope" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const orgIdFieldName = "org_id" + +var ( + reInvalidID = regexp.MustCompile("[^A-Za-z0-9]") + + // TODO: Remove once auth methods are in + OrgScope string + RWDb = new(atomic.Value) +) + +func init() { + RWDb.Store((*db.Db)(nil)) +} + +// Service handles request as described by the pbs.OrgServiceServer interface. +type Service struct { + iamRepo common.IamRepoFactory + authTokenRepo common.AuthTokenRepoFactory +} + +// NewService returns an org service which handles org related requests to watchtower. +func NewService(iamRepo common.IamRepoFactory, atRepo common.AuthTokenRepoFactory) (Service, error) { + if iamRepo == nil { + return Service{}, fmt.Errorf("nil iam repository provided") + } + if atRepo == nil { + return Service{}, fmt.Errorf("nil auth token repository provided") + } + + return Service{iamRepo: iamRepo, authTokenRepo: atRepo}, nil +} + +var _ pbs.AuthenticationServiceServer = Service{} + +// Authenticate implements the interface pbs.AuthenticationServiceServer. +func (s Service) Authenticate(ctx context.Context, req *pbs.AuthenticateRequest) (*pbs.AuthenticateResponse, error) { + if err := validateAuthenticateRequest(req); err != nil { + return nil, err + } + tok, err := s.authenticateWithRepo(ctx, req) + if err != nil { + return nil, err + } + return &pbs.AuthenticateResponse{Item: tok}, nil +} + +// Deauthenticate implements the interface pbs.AuthenticationServiceServer. +func (s Service) Deauthenticate(ctx context.Context, req *pbs.DeauthenticateRequest) (*pbs.DeauthenticateResponse, error) { + return nil, status.Error(codes.Unimplemented, "Requested method is unimplemented for Org.") +} + +func (s Service) authenticateWithRepo(ctx context.Context, req *pbs.AuthenticateRequest) (*pba.AuthToken, error) { + userRepo, err := s.iamRepo() + if err != nil { + return nil, err + } + atRepo, err := s.authTokenRepo() + if err != nil { + return nil, err + } + // Place holder for making a request to authenticate + creds := req.GetCredentials().GetFields() + pwName, password := creds["name"], creds["password"] + if pwName.GetStringValue() == "wrong" && password.GetStringValue() == "wrong" { + return nil, status.Error(codes.Unauthenticated, "Unable to authenticate.") + } + // Get back a password.Account with a CredentialId string and a public Id + // Create a fake auth account ID for the moment + acctIdBytes := sha256.Sum256([]byte(pwName.GetStringValue())) + acctId := fmt.Sprintf("aa_%s", base32.StdEncoding.EncodeToString(acctIdBytes[:])[0:10]) + + aAcct := &iam.AuthAccount{AuthAccount: &iamStore.AuthAccount{ + PublicId: acctId, + ScopeId: OrgScope, + AuthMethodId: req.AuthMethodId, + }} + RWDb.Load().(*db.Db).Create(ctx, aAcct) + + u, err := userRepo.LookupUserWithLogin(ctx, acctId, iam.WithAutoVivify(true)) + if err != nil { + return nil, err + } + tok, err := atRepo.CreateAuthToken(ctx, u.GetPublicId(), acctId) + if err != nil { + return nil, err + } + tok.Token = tok.GetPublicId() + "_" + tok.GetToken() + return toProto(tok), nil +} + +func toProto(t *authtoken.AuthToken) *pba.AuthToken { + return &pba.AuthToken{ + Id: t.GetPublicId(), + Token: t.GetToken(), + UserId: t.GetIamUserId(), + AuthMethodId: t.GetAuthMethodId(), + CreatedTime: t.GetCreateTime().GetTimestamp(), + UpdatedTime: t.GetUpdateTime().GetTimestamp(), + ApproximateLastUsedTime: t.GetApproximateLastAccessTime().GetTimestamp(), + ExpirationTime: t.GetExpirationTime().GetTimestamp(), + } +} + +// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce +// requirements on the structure of the request. They verify that: +// * The path passed in is correctly formatted +// * All required parameters are set +// * There are no conflicting parameters provided +func validateAuthenticateRequest(req *pbs.AuthenticateRequest) error { + badFields := make(map[string]string) + if !validId(req.GetOrgId(), scope.Org.Prefix()+"_") { + badFields[orgIdFieldName] = "Invalid formatted identifier." + } + if strings.TrimSpace(req.GetAuthMethodId()) == "" { + badFields["auth_method_id"] = "This is a required field." + } else if validId(req.GetAuthMethodId(), "am") { + badFields["auth_method_id"] = "Invalid formatted identifier." + } + // TODO: Update this when we enable different auth method types. + if req.GetCredentials() == nil { + badFields["credentials"] = "This is a required field." + } + // TODO: Update this when we enable split cookie token types. + tType := strings.ToLower(strings.TrimSpace(req.GetTokenType())) + if tType != "" && tType != "token" { + badFields["token_type"] = "The only accepted type is 'token'." + } + if len(badFields) > 0 { + return handlers.InvalidArgumentErrorf("Invalid fields provided in request.", badFields) + } + return nil +} + +func validId(id, prefix string) bool { + if !strings.HasPrefix(id, prefix) { + return false + } + id = strings.TrimPrefix(id, prefix) + return !reInvalidID.Match([]byte(id)) +} diff --git a/internal/servers/controller/handlers/authenticate/authenticate_service_test.go b/internal/servers/controller/handlers/authenticate/authenticate_service_test.go new file mode 100644 index 000000000..58e3be0a5 --- /dev/null +++ b/internal/servers/controller/handlers/authenticate/authenticate_service_test.go @@ -0,0 +1,236 @@ +package authenticate + +import ( + "context" + "crypto/sha256" + "encoding/base32" + "fmt" + "strings" + "testing" + + structpb "github.com/golang/protobuf/ptypes/struct" + "github.com/hashicorp/watchtower/internal/authtoken" + "github.com/hashicorp/watchtower/internal/db" + pba "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/authtokens" + pbs "github.com/hashicorp/watchtower/internal/gen/controller/api/services" + "github.com/hashicorp/watchtower/internal/iam" + iamStore "github.com/hashicorp/watchtower/internal/iam/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func getAuthMethodAndAccountId(t *testing.T, org *iam.Scope, rw *db.Db) (string, string) { + // TODO: Remove this when Auth Account repo is in place. + insert := `insert into auth_method + (public_id, scope_id) + values + ($1, $2);` + amId, err := db.NewPublicId("am") + require.NoError(t, err) + innerDb, err := rw.DB() + require.NoError(t, err) + _, err = innerDb.Exec(insert, amId, org.GetPublicId()) + require.NoError(t, err) + aAcctId, err := db.NewPublicId("aa") + require.NoError(t, err) + + aAcct := &iam.AuthAccount{AuthAccount: &iamStore.AuthAccount{ + PublicId: aAcctId, + ScopeId: org.GetPublicId(), + AuthMethodId: amId, + }} + require.NoError(t, rw.Create(context.Background(), aAcct)) + return aAcct.GetAuthMethodId(), aAcct.GetPublicId() +} + +func TestAuthenticate(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + RWDb.Store(rw) + wrapper := db.TestWrapper(t) + o, _ := iam.TestScopes(t, conn) + + authTokenRepoFn := func() (*authtoken.Repository, error) { return authtoken.NewRepository(rw, rw, wrapper) } + iamRepoFn := func() (*iam.Repository, error) { return iam.NewRepository(rw, rw, wrapper) } + amId, _ := getAuthMethodAndAccountId(t, o, rw) + + cases := []struct { + name string + request *pbs.AuthenticateRequest + want *pbs.AuthenticateResponse + wantErr error + }{ + { + name: "basic", + request: &pbs.AuthenticateRequest{ + OrgId: o.GetPublicId(), + AuthMethodId: amId, + TokenType: "token", + Credentials: func() *structpb.Struct { + creds := map[string]*structpb.Value{ + "name": {Kind: &structpb.Value_StringValue{StringValue: "admin"}}, + "password": {Kind: &structpb.Value_StringValue{StringValue: "hunter2"}}, + } + return &structpb.Struct{Fields: creds} + }(), + }, + want: &pbs.AuthenticateResponse{Item: &pba.AuthToken{ + AuthMethodId: amId, + }}, + }, + { + name: "no-token-type", + request: &pbs.AuthenticateRequest{ + OrgId: o.GetPublicId(), + AuthMethodId: amId, + Credentials: func() *structpb.Struct { + creds := map[string]*structpb.Value{ + "name": {Kind: &structpb.Value_StringValue{StringValue: "admin"}}, + "password": {Kind: &structpb.Value_StringValue{StringValue: "hunter2"}}, + } + return &structpb.Struct{Fields: creds} + }(), + }, + want: &pbs.AuthenticateResponse{Item: &pba.AuthToken{ + AuthMethodId: amId, + }}, + }, + { + name: "bad-token-type", + request: &pbs.AuthenticateRequest{ + OrgId: o.GetPublicId(), + AuthMethodId: amId, + TokenType: "email", + Credentials: func() *structpb.Struct { + creds := map[string]*structpb.Value{ + "name": {Kind: &structpb.Value_StringValue{StringValue: "admin"}}, + "password": {Kind: &structpb.Value_StringValue{StringValue: "hunter2"}}, + } + return &structpb.Struct{Fields: creds} + }(), + }, + wantErr: status.Error(codes.InvalidArgument, "invalid argument"), + }, + { + name: "no-authmethod", + request: &pbs.AuthenticateRequest{ + OrgId: o.GetPublicId(), + Credentials: func() *structpb.Struct { + creds := map[string]*structpb.Value{ + "name": {Kind: &structpb.Value_StringValue{StringValue: "admin"}}, + "password": {Kind: &structpb.Value_StringValue{StringValue: "hunter2"}}, + } + return &structpb.Struct{Fields: creds} + }(), + }, + wantErr: status.Error(codes.InvalidArgument, "invalid argument"), + }, + { + name: "wrong-username-password", + request: &pbs.AuthenticateRequest{ + OrgId: o.GetPublicId(), + AuthMethodId: amId, + TokenType: "token", + Credentials: func() *structpb.Struct { + creds := map[string]*structpb.Value{ + "name": {Kind: &structpb.Value_StringValue{StringValue: "wrong"}}, + "password": {Kind: &structpb.Value_StringValue{StringValue: "wrong"}}, + } + return &structpb.Struct{Fields: creds} + }(), + }, + wantErr: status.Error(codes.Unauthenticated, "unauthenticated"), + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + s, err := NewService(iamRepoFn, authTokenRepoFn) + require.NoError(err) + + // TODO: remove when we stop faking things + name := tc.request.Credentials.Fields["name"].GetStringValue() + acctIdBytes := sha256.Sum256([]byte(name)) + acctId := fmt.Sprintf("aa_%s", base32.StdEncoding.EncodeToString(acctIdBytes[:])[0:10]) + + aAcct := &iam.AuthAccount{AuthAccount: &iamStore.AuthAccount{ + PublicId: acctId, + ScopeId: o.PublicId, + AuthMethodId: amId, + }} + RWDb.Load().(*db.Db).Create(context.Background(), aAcct) + + resp, err := s.Authenticate(context.Background(), tc.request) + if tc.wantErr != nil { + assert.Error(err) + assert.Equal(status.Code(tc.wantErr), status.Code(err)) + return + } + require.NoError(err) + aToken := resp.GetItem() + assert.NotEmpty(aToken.GetId()) + assert.NotEmpty(aToken.GetToken()) + assert.True(strings.HasPrefix(aToken.GetToken(), aToken.GetId())) + assert.Equal(amId, aToken.GetAuthMethodId()) + assert.Equal(aToken.GetCreatedTime(), aToken.GetUpdatedTime()) + assert.Equal(aToken.GetCreatedTime(), aToken.GetApproximateLastUsedTime()) + }) + } +} + +func TestAuthenticate_AuthAccountConnectedToIamUser(t *testing.T) { + assert, require := assert.New(t), require.New(t) + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + RWDb.Store(rw) + wrapper := db.TestWrapper(t) + o, _ := iam.TestScopes(t, conn) + + authTokenRepoFn := func() (*authtoken.Repository, error) { return authtoken.NewRepository(rw, rw, wrapper) } + iamRepoFn := func() (*iam.Repository, error) { return iam.NewRepository(rw, rw, wrapper) } + iamRepo, err := iamRepoFn() + require.NoError(err) + + // connected to an account. + amId, _ := getAuthMethodAndAccountId(t, o, rw) + + name := "admin" + acctIdBytes := sha256.Sum256([]byte(name)) + acctId := fmt.Sprintf("aa_%s", base32.StdEncoding.EncodeToString(acctIdBytes[:])[0:10]) + + aAcct := &iam.AuthAccount{AuthAccount: &iamStore.AuthAccount{ + PublicId: acctId, + ScopeId: o.PublicId, + AuthMethodId: amId, + }} + RWDb.Load().(*db.Db).Create(context.Background(), aAcct) + + iamUser, err := iamRepo.LookupUserWithLogin(context.Background(), acctId, iam.WithAutoVivify(true)) + require.NoError(err) + + s, err := NewService(iamRepoFn, authTokenRepoFn) + require.NoError(err) + resp, err := s.Authenticate(context.Background(), &pbs.AuthenticateRequest{ + OrgId: o.GetPublicId(), + AuthMethodId: amId, + Credentials: func() *structpb.Struct { + creds := map[string]*structpb.Value{ + "name": {Kind: &structpb.Value_StringValue{StringValue: name}}, + "password": {Kind: &structpb.Value_StringValue{StringValue: "hunter2"}}, + } + return &structpb.Struct{Fields: creds} + }(), + }) + require.NoError(err) + + aToken := resp.GetItem() + assert.Equal(iamUser.GetPublicId(), aToken.GetUserId()) + assert.Equal(amId, aToken.GetAuthMethodId()) + + assert.NotEmpty(aToken.GetId()) + assert.NotEmpty(aToken.GetToken()) + assert.True(strings.HasPrefix(aToken.GetToken(), aToken.GetId())) +} diff --git a/internal/servers/controller/handlers/authtoken_intercept.go b/internal/servers/controller/handlers/authtoken_intercept.go index 5fd016e3e..05bd01b88 100644 --- a/internal/servers/controller/handlers/authtoken_intercept.go +++ b/internal/servers/controller/handlers/authtoken_intercept.go @@ -21,46 +21,110 @@ const ( // TokenAuthenticator returns a function that can be used in grpc-gateway's runtime.WithMetadata ServerOption. // It looks at the cookies and headers of the incoming request and returns metadata that can later be // used by handlers to build a TokenMetadata using the ToTokenMetadata function. -func TokenAuthenticator(l hclog.Logger, tokenRepo common.AuthTokenRepoFactory) func(context.Context, *http.Request) metadata.MD { +func TokenAuthenticator(l hclog.Logger, tokenRepo common.AuthTokenRepoFactory, iamRepo common.IamRepoFactory) func(context.Context, *http.Request) metadata.MD { return func(ctx context.Context, req *http.Request) metadata.MD { - tMD := TokenMetadata{} - if authHeader := req.Header.Get(headerAuthMethod); authHeader != "" { - headerSplit := strings.SplitN(strings.TrimSpace(authHeader), " ", 2) - if len(headerSplit) == 2 && strings.EqualFold(strings.TrimSpace(headerSplit[0]), "bearer") { - tMD.recievedTokenType = authTokenTypeBearer - tMD.bearerPayload = strings.TrimSpace(headerSplit[1]) + return TokenMetadata{}.toMetadata() + /* + tMD := TokenMetadata{} + if authHeader := req.Header.Get(headerAuthMethod); authHeader != "" { + headerSplit := strings.SplitN(strings.TrimSpace(authHeader), " ", 2) + if len(headerSplit) == 2 && strings.EqualFold(strings.TrimSpace(headerSplit[0]), "bearer") { + tMD.receivedTokenType = authTokenTypeBearer + tMD.bearerPayload = strings.TrimSpace(headerSplit[1]) + } } - } - if tMD.recievedTokenType != authTokenTypeBearer { - if hc, err := req.Cookie(httpOnlyCookieName); err == nil { - tMD.httpCookiePayload = hc.Value + if tMD.receivedTokenType != authTokenTypeBearer { + if hc, err := req.Cookie(httpOnlyCookieName); err == nil { + tMD.httpCookiePayload = hc.Value + } + if jc, err := req.Cookie(jsVisibleCookieName); err == nil { + tMD.jsCookiePayload = jc.Value + } + if tMD.httpCookiePayload != "" && tMD.jsCookiePayload != "" { + tMD.receivedTokenType = authTokenTypeSplitCookie + } } - if jc, err := req.Cookie(jsVisibleCookieName); err == nil { - tMD.jsCookiePayload = jc.Value - } - if tMD.httpCookiePayload != "" && tMD.jsCookiePayload != "" { - tMD.recievedTokenType = authTokenTypeSplitCookie - } - } - if tMD.recievedTokenType == authTokenTypeUnknown || tMD.token() == "" || tMD.publicId() == "" { + if tMD.receivedTokenType == authTokenTypeUnknown || tMD.token() == "" || tMD.publicId() == "" { + return tMD.toMetadata() + } + + tokenRepo, err := tokenRepo() + if err != nil { + l.Error("failed to get authtoken repo", "error", err) + return tMD.toMetadata() + } + + at, err := tokenRepo.ValidateToken(ctx, tMD.publicId(), tMD.token()) + if err != nil { + l.Error("failed to validate token", "error", err) + } + if at != nil { + tMD.UserId = at.GetIamUserId() + } else { + tMD.UserId = "u_anon" + } + + iamRepo, err := iamRepo() + if err != nil { + l.Error("failed to get iam repo", "error", err) + return tMD.toMetadata() + } + grantPairs, err := iamRepo.GrantsForUser(ctx, tMD.UserId) + if err != nil { + l.Error("failed to query for user grants", "error", err) + return tMD.toMetadata() + } + parsedGrants := make([]perms.Grant, 0, len(grantPairs)) + for _, pair := range grantPairs { + parsed, err := perms.Parse(pair.ScopeId, tMD.UserId, pair.Grant) + if err != nil { + l.Error("failed to parse grant", "grant", pair.Grant, "error", err) + return tMD.toMetadata() + } + parsedGrants = append(parsedGrants, parsed) + } + + var scopeId, id, pin string + var typ resource.Type + var act action.Type + typRaw := ctx.Value(globals.ContextTypeValue) + if typRaw != nil { + typ = typRaw.(resource.Type) + } + scopeIdRaw := ctx.Value(globals.ContextScopeValue) + if scopeIdRaw != nil { + scopeId = scopeIdRaw.(string) + } + actRaw := ctx.Value(globals.ContextActionValue) + if actRaw != nil { + act = actRaw.(action.Type) + } + idRaw := ctx.Value(globals.ContextResourceValue) + if idRaw != nil { + id = idRaw.(string) + } + pinRaw := ctx.Value(globals.ContextPinValue) + if pinRaw != nil { + pin = pinRaw.(string) + } + + acl := perms.NewACL(parsedGrants...) + res := perms.Resource{ + // These values were parsed from the request information + ScopeId: scopeId, + Type: typ, + Id: id, + Pin: pin, + } + allowed := acl.Allowed(res, act) + if allowed.Allowed { + tMD.authorized = true + } + l.Info("grant checking", "user_id", tMD.UserId, "grants", pretty.Sprint(grantPairs), "resource", res, "authorized", allowed) + return tMD.toMetadata() - } - - repo, err := tokenRepo() - if err != nil { - l.Error("failed to get authtoken repo", "error", err) - return tMD.toMetadata() - } - at, err := repo.ValidateToken(ctx, tMD.publicId(), tMD.token()) - if err != nil { - l.Error("failed to validate token", "error", err) - } - if at != nil { - tMD.UserId = at.GetIamUserId() - } - - return tMD.toMetadata() + */ } } @@ -87,7 +151,9 @@ type TokenMetadata struct { // Only set the UserId if the token was found and was not expired. UserId string - recievedTokenType tokenFormat + authorized bool + + receivedTokenType tokenFormat bearerPayload string jsCookiePayload string @@ -96,6 +162,7 @@ type TokenMetadata struct { const ( mdAuthTokenUserKey = "wt-authtoken-user-key" + mdAuthAuthorizedKey = "wt-authtoken-authorized" mdAuthTokenBearerTokenKey = "wt-authtoken-bearer-token-key" mdAuthTokenHttpTokenKey = "wt-authtoken-http-token-key" mdAuthTokenJsTokenKey = "wt-authtoken-js-token-key" @@ -113,6 +180,9 @@ func ToTokenMetadata(ctx context.Context) TokenMetadata { if uid := md.Get(mdAuthTokenUserKey); len(uid) > 0 { tMD.UserId = uid[0] } + if authzd := md.Get(mdAuthAuthorizedKey); len(authzd) > 0 { + tMD.authorized = authzd[0] == "true" + } if token := md.Get(mdAuthTokenBearerTokenKey); len(token) > 0 { tMD.bearerPayload = token[0] } @@ -124,7 +194,7 @@ func ToTokenMetadata(ctx context.Context) TokenMetadata { } if sType := md.Get(mdAuthTokenTypeKey); len(sType) > 0 { if st, err := strconv.Atoi(sType[0]); err == nil { - tMD.recievedTokenType = tokenFormat(st) + tMD.receivedTokenType = tokenFormat(st) } } @@ -136,6 +206,11 @@ func (s TokenMetadata) toMetadata() metadata.MD { if s.UserId != "" { md.Set(mdAuthTokenUserKey, s.UserId) } + if s.authorized { + md.Set(mdAuthAuthorizedKey, "true") + } else { + md.Set(mdAuthAuthorizedKey, "false") + } if s.bearerPayload != "" { md.Set(mdAuthTokenBearerTokenKey, s.bearerPayload) } @@ -145,9 +220,10 @@ func (s TokenMetadata) toMetadata() metadata.MD { if s.jsCookiePayload != "" { md.Set(mdAuthTokenJsTokenKey, s.jsCookiePayload) } - if s.recievedTokenType != authTokenTypeUnknown { - md.Set(mdAuthTokenTypeKey, fmt.Sprint(s.recievedTokenType)) + if s.receivedTokenType != authTokenTypeUnknown { + md.Set(mdAuthTokenTypeKey, fmt.Sprint(s.receivedTokenType)) } + return md } @@ -155,7 +231,7 @@ func (s TokenMetadata) toMetadata() metadata.MD { // is malformed then this returns an empty string. func (s TokenMetadata) publicId() string { tok := "" - switch s.recievedTokenType { + switch s.receivedTokenType { case authTokenTypeBearer: tok = s.bearerPayload case authTokenTypeSplitCookie: @@ -172,7 +248,7 @@ func (s TokenMetadata) publicId() string { // is malformed then this returns an empty string. func (s TokenMetadata) token() string { var tok string - switch s.recievedTokenType { + switch s.receivedTokenType { case authTokenTypeBearer: tok = s.bearerPayload case authTokenTypeSplitCookie: diff --git a/internal/servers/controller/handlers/authtoken_intercept_test.go b/internal/servers/controller/handlers/authtoken_intercept_test.go index ab26b762f..b9d5e10e9 100644 --- a/internal/servers/controller/handlers/authtoken_intercept_test.go +++ b/internal/servers/controller/handlers/authtoken_intercept_test.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/watchtower/internal/db" "github.com/hashicorp/watchtower/internal/gen/controller/api/services" pbs "github.com/hashicorp/watchtower/internal/gen/controller/api/services" + "github.com/hashicorp/watchtower/internal/iam" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -30,6 +31,7 @@ func (s *fakeHandler) GetOrg(ctx context.Context, _ *pbs.GetOrgRequest) (*pbs.Ge } func TestAuthTokenPublicIdTokenValue(t *testing.T) { + t.Skip("functionality has moved for now") cases := []struct { name string in TokenMetadata @@ -39,7 +41,7 @@ func TestAuthTokenPublicIdTokenValue(t *testing.T) { { name: "no delimeter", in: TokenMetadata{ - recievedTokenType: authTokenTypeBearer, + receivedTokenType: authTokenTypeBearer, bearerPayload: "prefix_publicid_token", jsCookiePayload: "this_is_just_junk", httpCookiePayload: "this_can_be_ignored", @@ -50,7 +52,7 @@ func TestAuthTokenPublicIdTokenValue(t *testing.T) { { name: "no delimeter", in: TokenMetadata{ - recievedTokenType: authTokenTypeSplitCookie, + receivedTokenType: authTokenTypeSplitCookie, bearerPayload: "this_is_just_junk_that_should_be_ignored", jsCookiePayload: "prefix_publicid_token", httpCookiePayload: "cookiepayload", @@ -61,7 +63,7 @@ func TestAuthTokenPublicIdTokenValue(t *testing.T) { { name: "no delimeter", in: TokenMetadata{ - recievedTokenType: authTokenTypeBearer, + receivedTokenType: authTokenTypeBearer, bearerPayload: "this-doesnt-have-the-expected-delimiter", }, wantId: "", @@ -70,7 +72,7 @@ func TestAuthTokenPublicIdTokenValue(t *testing.T) { { name: "to many delimeters", in: TokenMetadata{ - recievedTokenType: authTokenTypeBearer, + receivedTokenType: authTokenTypeBearer, bearerPayload: "this_has_to_many_delimiters", }, wantId: "", @@ -87,13 +89,19 @@ func TestAuthTokenPublicIdTokenValue(t *testing.T) { } func TestAuthTokenAuthenticator(t *testing.T) { + t.Skip("functionality has moved for now") conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) - repo, err := authtoken.NewRepository(rw, rw, wrapper) + tokenRepo, err := authtoken.NewRepository(rw, rw, wrapper) require.NoError(t, err) - repoFn := func() (*authtoken.Repository, error) { - return repo, nil + iamRepo, err := iam.NewRepository(rw, rw, wrapper) + require.NoError(t, err) + tokenRepoFn := func() (*authtoken.Repository, error) { + return tokenRepo, nil + } + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil } at := authtoken.TestAuthToken(t, conn, wrapper) @@ -110,13 +118,13 @@ func TestAuthTokenAuthenticator(t *testing.T) { { name: "Empty headers", headers: map[string]string{}, - wantAuthTokMd: TokenMetadata{recievedTokenType: authTokenTypeUnknown}, + wantAuthTokMd: TokenMetadata{receivedTokenType: authTokenTypeUnknown}, }, { name: "Bear token", headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", tokValue)}, wantAuthTokMd: TokenMetadata{ - recievedTokenType: authTokenTypeBearer, + receivedTokenType: authTokenTypeBearer, bearerPayload: tokValue, UserId: at.GetIamUserId(), }, @@ -128,7 +136,7 @@ func TestAuthTokenAuthenticator(t *testing.T) { {Name: jsVisibleCookieName, Value: jsCookieVal}, }, wantAuthTokMd: TokenMetadata{ - recievedTokenType: authTokenTypeSplitCookie, + receivedTokenType: authTokenTypeSplitCookie, httpCookiePayload: httpCookieVal, jsCookiePayload: jsCookieVal, UserId: at.GetIamUserId(), @@ -140,7 +148,7 @@ func TestAuthTokenAuthenticator(t *testing.T) { {Name: httpOnlyCookieName, Value: httpCookieVal}, }, wantAuthTokMd: TokenMetadata{ - recievedTokenType: authTokenTypeUnknown, + receivedTokenType: authTokenTypeUnknown, httpCookiePayload: httpCookieVal, }, }, @@ -150,7 +158,7 @@ func TestAuthTokenAuthenticator(t *testing.T) { {Name: jsVisibleCookieName, Value: jsCookieVal}, }, wantAuthTokMd: TokenMetadata{ - recievedTokenType: authTokenTypeUnknown, + receivedTokenType: authTokenTypeUnknown, jsCookiePayload: jsCookieVal, }, }, @@ -163,7 +171,7 @@ func TestAuthTokenAuthenticator(t *testing.T) { }, // We prioritize the auth header over the cookie and if the header is set we ignore the cookies completely. wantAuthTokMd: TokenMetadata{ - recievedTokenType: authTokenTypeBearer, + receivedTokenType: authTokenTypeBearer, bearerPayload: tokValue, UserId: at.GetIamUserId(), }, @@ -176,7 +184,7 @@ func TestAuthTokenAuthenticator(t *testing.T) { tMD := ToTokenMetadata(ctx) assert.Equal(t, tc.wantAuthTokMd, tMD) }} - mux := runtime.NewServeMux(runtime.WithMetadata(TokenAuthenticator(hclog.L(), repoFn))) + mux := runtime.NewServeMux(runtime.WithMetadata(TokenAuthenticator(hclog.L(), tokenRepoFn, iamRepoFn))) require.NoError(t, services.RegisterOrgServiceHandlerServer(context.Background(), mux, hook)) req := httptest.NewRequest("GET", "http://127.0.0.1/v1/orgs/1", nil) diff --git a/internal/servers/controller/handlers/orgs/org_service.go b/internal/servers/controller/handlers/orgs/org_service.go index 8cfa0f48d..0a20b183b 100644 --- a/internal/servers/controller/handlers/orgs/org_service.go +++ b/internal/servers/controller/handlers/orgs/org_service.go @@ -12,8 +12,6 @@ import ( "github.com/hashicorp/watchtower/internal/servers/controller/common" "github.com/hashicorp/watchtower/internal/servers/controller/handlers" "github.com/hashicorp/watchtower/internal/types/scope" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" ) @@ -63,16 +61,6 @@ func (s Service) GetOrg(ctx context.Context, req *pbs.GetOrgRequest) (*pbs.GetOr return &pbs.GetOrgResponse{Item: o}, nil } -// Authenticate implements the interface pbs.OrgServiceServer. -func (s Service) Authenticate(ctx context.Context, req *pbs.AuthenticateRequest) (*pbs.AuthenticateResponse, error) { - return nil, status.Error(codes.Unimplemented, "Requested method is unimplemented for Orgs.") -} - -// Deauthenticate implements the interface pbs.OrgServiceServer. -func (s Service) Deauthenticate(ctx context.Context, req *pbs.DeauthenticateRequest) (*pbs.DeauthenticateResponse, error) { - return nil, status.Error(codes.Unimplemented, "Requested method is unimplemented for Orgs.") -} - func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Org, error) { repo, err := s.repo() if err != nil { diff --git a/internal/servers/controller/handlers/roles/role_service.go b/internal/servers/controller/handlers/roles/role_service.go index c23e985ae..fa56f942f 100644 --- a/internal/servers/controller/handlers/roles/role_service.go +++ b/internal/servers/controller/handlers/roles/role_service.go @@ -130,7 +130,7 @@ func (s Service) AddRolePrincipals(ctx context.Context, req *pbs.AddRolePrincipa if err := validateAddRolePrincipalsRequest(req); err != nil { return nil, err } - r, err := s.addPrinciplesInRepo(ctx, req.GetRoleId(), req.GetUserIds(), req.GetGroupIds(), req.GetVersion().GetValue()) + r, err := s.addPrinciplesInRepo(ctx, req.GetRoleId(), req.GetPrincipalIds(), req.GetVersion().GetValue()) if err != nil { return nil, err } @@ -144,7 +144,7 @@ func (s Service) SetRolePrincipals(ctx context.Context, req *pbs.SetRolePrincipa if err := validateSetRolePrincipalsRequest(req); err != nil { return nil, err } - r, err := s.setPrinciplesInRepo(ctx, req.GetRoleId(), req.GetUserIds(), req.GetGroupIds(), req.GetVersion().GetValue()) + r, err := s.setPrinciplesInRepo(ctx, req.GetRoleId(), req.GetPrincipalIds(), req.GetVersion().GetValue()) if err != nil { return nil, err } @@ -158,7 +158,7 @@ func (s Service) RemoveRolePrincipals(ctx context.Context, req *pbs.RemoveRolePr if err := validateRemoveRolePrincipalsRequest(req); err != nil { return nil, err } - r, err := s.removePrinciplesInRepo(ctx, req.GetRoleId(), req.GetUserIds(), req.GetGroupIds(), req.GetVersion().GetValue()) + r, err := s.removePrinciplesInRepo(ctx, req.GetRoleId(), req.GetPrincipalIds(), req.GetVersion().GetValue()) if err != nil { return nil, err } @@ -325,12 +325,12 @@ func (s Service) listFromRepo(ctx context.Context, scopeId string) ([]*pb.Role, return outRl, nil } -func (s Service) addPrinciplesInRepo(ctx context.Context, roleId string, userIds []string, groupIds []string, version uint32) (*pb.Role, error) { +func (s Service) addPrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*pb.Role, error) { repo, err := s.repoFn() if err != nil { return nil, err } - _, err = repo.AddPrincipalRoles(ctx, roleId, version, userIds, groupIds) + _, err = repo.AddPrincipalRoles(ctx, roleId, version, principalIds) if err != nil { return nil, status.Errorf(codes.Internal, "Unable to add principles to role: %v.", err) } @@ -344,12 +344,12 @@ func (s Service) addPrinciplesInRepo(ctx context.Context, roleId string, userIds return toProto(out, pr, roleGrants), nil } -func (s Service) setPrinciplesInRepo(ctx context.Context, roleId string, userIds []string, groupIds []string, version uint32) (*pb.Role, error) { +func (s Service) setPrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*pb.Role, error) { repo, err := s.repoFn() if err != nil { return nil, err } - _, _, err = repo.SetPrincipalRoles(ctx, roleId, version, userIds, groupIds) + _, _, err = repo.SetPrincipalRoles(ctx, roleId, version, principalIds) if err != nil { return nil, status.Errorf(codes.Internal, "Unable to set principles on role: %v.", err) } @@ -363,12 +363,12 @@ func (s Service) setPrinciplesInRepo(ctx context.Context, roleId string, userIds return toProto(out, pr, roleGrants), nil } -func (s Service) removePrinciplesInRepo(ctx context.Context, roleId string, userIds []string, groupIds []string, version uint32) (*pb.Role, error) { +func (s Service) removePrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*pb.Role, error) { repo, err := s.repoFn() if err != nil { return nil, err } - _, err = repo.DeletePrincipalRoles(ctx, roleId, version, userIds, groupIds) + _, err = repo.DeletePrincipalRoles(ctx, roleId, version, principalIds) if err != nil { return nil, status.Errorf(codes.Internal, "Unable to remove principles from role: %v.", err) } @@ -476,12 +476,13 @@ func toProto(in *iam.Role, principals []iam.PrincipalRole, grants []*iam.RoleGra out.Name = &wrapperspb.StringValue{Value: in.GetName()} } for _, p := range principals { - switch p.GetType() { - case iam.UserRoleType.String(): - out.UserIds = append(out.UserIds, p.GetPrincipalId()) - case iam.GroupRoleType.String(): - out.GroupIds = append(out.GroupIds, p.GetPrincipalId()) + principal := &pb.Principal{ + Id: p.GetPrincipalId(), + Type: p.GetType(), + ScopeId: p.GetPrincipalScopeId(), } + out.Principals = append(out.Principals, principal) + out.PrincipalIds = append(out.PrincipalIds, p.GetPrincipalId()) } for _, g := range grants { out.Grants = append(out.Grants, g.GetRawGrant()) @@ -542,6 +543,9 @@ func validateCreateRequest(req *pbs.CreateRoleRequest) error { badFields["grant_scope_id"] = "Must be empty or set to the project_id when the scope type is project." } } + if item.GetPrincipals() != nil { + badFields["principals"] = "This is a read only field." + } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Argument errors found in the request.", badFields) } @@ -572,6 +576,9 @@ func validateUpdateRequest(req *pbs.UpdateRoleRequest) error { if item.GetUpdatedTime() != nil { badFields["updated_time"] = "This is a read only field and cannot be specified in an update request." } + if item.GetPrincipals() != nil { + badFields["principals"] = "This is a read only field and cannot be specified in an update request." + } if item.GetGrantScopeId() != nil && req.ProjectId != "" { if item.GetGrantScopeId().Value != req.ProjectId { badFields["grant_scope_id"] = "Must be empty or set to the project_id when the scope type is project." @@ -611,9 +618,8 @@ func validateAddRolePrincipalsRequest(req *pbs.AddRolePrincipalsRequest) error { if req.GetVersion() == nil { badFields["version"] = "Required field." } - if len(req.GetGroupIds()) == 0 && len(req.GetUserIds()) == 0 { - badFields["user_ids"] = "Either user_ids or group_ids must be non empty." - badFields["group_ids"] = "Either user_ids or group_ids must be non empty." + if len(req.GetPrincipalIds()) == 0 { + badFields["principal_ids"] = "Must be non-empty." } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) @@ -643,9 +649,8 @@ func validateRemoveRolePrincipalsRequest(req *pbs.RemoveRolePrincipalsRequest) e if req.GetVersion() == nil { badFields["version"] = "Required field." } - if len(req.GetGroupIds()) == 0 && len(req.GetUserIds()) == 0 { - badFields["user_ids"] = "Either user_ids or group_ids must be non empty." - badFields["group_ids"] = "Either user_ids or group_ids must be non empty." + if len(req.GetPrincipalIds()) == 0 { + badFields["principal_ids"] = "Must be non-empty." } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) @@ -662,7 +667,7 @@ func validateAddRoleGrantsRequest(req *pbs.AddRoleGrantsRequest) error { badFields["version"] = "Required field." } if len(req.GetGrants()) == 0 { - badFields["grants"] = "This must be non empty." + badFields["grants"] = "Must be non-empty." } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) @@ -693,7 +698,7 @@ func validateRemoveRoleGrantsRequest(req *pbs.RemoveRoleGrantsRequest) error { badFields["version"] = "Required field." } if len(req.GetGrants()) == 0 { - badFields["grants"] = "This must be non empty." + badFields["grants"] = "Must be non-empty." } if len(badFields) > 0 { return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) diff --git a/internal/servers/controller/handlers/roles/role_service_test.go b/internal/servers/controller/handlers/roles/role_service_test.go index 2b57df2e9..823679733 100644 --- a/internal/servers/controller/handlers/roles/role_service_test.go +++ b/internal/servers/controller/handlers/roles/role_service_test.go @@ -38,6 +38,30 @@ func createDefaultRolesAndRepo(t *testing.T) (*iam.Role, *iam.Role, func() (*iam return or, pr, repoFn } +func equalPrincipals(role *pb.Role, principals []string) bool { + if len(role.Principals) != len(principals) { + return false + } + for _, principal := range principals { + var foundInPrincipals bool + var foundInPrincipalIds bool + for _, v := range role.Principals { + if v.Id == principal { + foundInPrincipals = true + } + } + for _, v := range role.PrincipalIds { + if v == principal { + foundInPrincipalIds = true + } + } + if !foundInPrincipals || !foundInPrincipalIds { + return false + } + } + return true +} + func TestGet(t *testing.T) { assert := assert.New(t) require := require.New(t) @@ -891,11 +915,10 @@ func TestAddPrincipal(t *testing.T) { role := iam.TestRole(t, conn, scope.GetPublicId()) tc.setup(role) req := &pbs.AddRolePrincipalsRequest{ - OrgId: o.GetPublicId(), - RoleId: role.GetPublicId(), - Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, - UserIds: tc.addUsers, - GroupIds: tc.addGroups, + OrgId: o.GetPublicId(), + RoleId: role.GetPublicId(), + Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, + PrincipalIds: append(tc.addUsers, tc.addGroups...), } got, err := s.AddRolePrincipals(context.Background(), req) @@ -907,8 +930,7 @@ func TestAddPrincipal(t *testing.T) { require.True(t, ok) require.NoError(t, err, "Got error: %v", s) - assert.ElementsMatch(t, got.GetItem().GetUserIds(), tc.resultUsers) - assert.ElementsMatch(t, got.GetItem().GetGroupIds(), tc.resultGroups) + assert.True(t, equalPrincipals(got.GetItem(), append(tc.resultUsers, tc.resultGroups...))) }) } } @@ -926,7 +948,6 @@ func TestAddPrincipal(t *testing.T) { OrgId: "", ProjectId: role.GetScopeId(), RoleId: role.GetPublicId(), - GroupIds: []string{}, Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, }, errCode: codes.InvalidArgument, @@ -937,7 +958,6 @@ func TestAddPrincipal(t *testing.T) { OrgId: o.GetPublicId(), ProjectId: "bad id", RoleId: role.GetPublicId(), - GroupIds: []string{}, Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, }, errCode: codes.InvalidArgument, @@ -948,7 +968,6 @@ func TestAddPrincipal(t *testing.T) { OrgId: o.GetPublicId(), ProjectId: role.GetScopeId(), RoleId: "bad id", - GroupIds: []string{}, Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, }, errCode: codes.InvalidArgument, @@ -1039,11 +1058,10 @@ func TestSetPrincipal(t *testing.T) { role := iam.TestRole(t, conn, scope.GetPublicId()) tc.setup(role) req := &pbs.SetRolePrincipalsRequest{ - OrgId: o.GetPublicId(), - RoleId: role.GetPublicId(), - Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, - UserIds: tc.setUsers, - GroupIds: tc.setGroups, + OrgId: o.GetPublicId(), + RoleId: role.GetPublicId(), + Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, + PrincipalIds: append(tc.setUsers, tc.setGroups...), } got, err := s.SetRolePrincipals(context.Background(), req) @@ -1055,8 +1073,7 @@ func TestSetPrincipal(t *testing.T) { require.True(t, ok) require.NoError(t, err, "Got error: %v", s) - assert.Equal(t, got.GetItem().GetUserIds(), tc.resultUsers) - assert.Equal(t, got.GetItem().GetGroupIds(), tc.resultGroups) + assert.True(t, equalPrincipals(got.GetItem(), append(tc.resultUsers, tc.resultGroups...))) }) } } @@ -1074,7 +1091,6 @@ func TestSetPrincipal(t *testing.T) { OrgId: "", ProjectId: role.GetScopeId(), RoleId: role.GetPublicId(), - GroupIds: []string{}, Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, }, errCode: codes.InvalidArgument, @@ -1085,7 +1101,6 @@ func TestSetPrincipal(t *testing.T) { OrgId: o.GetPublicId(), ProjectId: "bad id", RoleId: role.GetPublicId(), - GroupIds: []string{}, Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, }, errCode: codes.InvalidArgument, @@ -1096,7 +1111,6 @@ func TestSetPrincipal(t *testing.T) { OrgId: o.GetPublicId(), ProjectId: role.GetScopeId(), RoleId: "bad id", - GroupIds: []string{}, Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, }, errCode: codes.InvalidArgument, @@ -1205,11 +1219,10 @@ func TestRemovePrincipal(t *testing.T) { role := iam.TestRole(t, conn, scope.GetPublicId()) tc.setup(role) req := &pbs.RemoveRolePrincipalsRequest{ - OrgId: o.GetPublicId(), - RoleId: role.GetPublicId(), - Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, - UserIds: tc.removeUsers, - GroupIds: tc.removeGroups, + OrgId: o.GetPublicId(), + RoleId: role.GetPublicId(), + Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, + PrincipalIds: append(tc.removeUsers, tc.removeGroups...), } got, err := s.RemoveRolePrincipals(context.Background(), req) @@ -1221,8 +1234,7 @@ func TestRemovePrincipal(t *testing.T) { require.True(t, ok) require.NoError(t, err, "Got error: %v", s) - assert.ElementsMatch(t, got.GetItem().GetUserIds(), tc.resultUsers) - assert.ElementsMatch(t, got.GetItem().GetGroupIds(), tc.resultGroups) + assert.True(t, equalPrincipals(got.GetItem(), append(tc.resultUsers, tc.resultGroups...))) }) } } @@ -1240,7 +1252,6 @@ func TestRemovePrincipal(t *testing.T) { OrgId: "", ProjectId: role.GetScopeId(), RoleId: role.GetPublicId(), - GroupIds: []string{}, Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, }, errCode: codes.InvalidArgument, @@ -1251,7 +1262,6 @@ func TestRemovePrincipal(t *testing.T) { OrgId: o.GetPublicId(), ProjectId: "bad id", RoleId: role.GetPublicId(), - GroupIds: []string{}, Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, }, errCode: codes.InvalidArgument, @@ -1262,7 +1272,6 @@ func TestRemovePrincipal(t *testing.T) { OrgId: o.GetPublicId(), ProjectId: role.GetScopeId(), RoleId: "bad id", - GroupIds: []string{}, Version: &wrapperspb.UInt32Value{Value: role.GetVersion()}, }, errCode: codes.InvalidArgument, diff --git a/internal/servers/controller/testing.go b/internal/servers/controller/testing.go index 194071caa..ea08fda3c 100644 --- a/internal/servers/controller/testing.go +++ b/internal/servers/controller/testing.go @@ -120,6 +120,11 @@ type TestControllerOpts struct { // If true, the controller will not be started DisableAutoStart bool + + // DisableAuthorizationFailures will still cause authz checks to be + // performed but they won't cause 403 Forbidden. Useful for API-level + // testing to avoid a lot of faff. + DisableAuthorizationFailures bool } func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController { @@ -183,8 +188,9 @@ func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController { } conf := &Config{ - RawConfig: opts.Config, - Server: tc.b, + RawConfig: opts.Config, + Server: tc.b, + DisableAuthorizationFailures: opts.DisableAuthorizationFailures, } tc.c, err = New(conf) diff --git a/internal/servers/worker/config.go b/internal/servers/worker/config.go index bef8b7e2c..e318a18cd 100644 --- a/internal/servers/worker/config.go +++ b/internal/servers/worker/config.go @@ -1,14 +1,11 @@ package worker import ( - "context" - "github.com/hashicorp/watchtower/internal/cmd/base" "github.com/hashicorp/watchtower/internal/cmd/config" ) type Config struct { *base.Server - RawConfig *config.Config - BaseContext context.Context + RawConfig *config.Config } diff --git a/internal/types/action/action.go b/internal/types/action/action.go index e4fc9aaf9..b7e9e0f15 100644 --- a/internal/types/action/action.go +++ b/internal/types/action/action.go @@ -21,24 +21,25 @@ const ( AddPrincipals Type = 12 SetPrincipals Type = 13 RemovePrincipals Type = 14 + Deauthenticate Type = 15 ) var Map = map[string]Type{ - "unknown": Unknown, - "list": List, - "create": Create, - "update": Update, - "read": Read, - "delete": Delete, - "authenticate": Authenticate, - "*": All, - "connect": Connect, - "add-grants": AddGrants, - "remove-grants": RemoveGrants, - "set-grants": SetGrants, - "add-principals": AddPrincipals, - "set-principals": SetPrincipals, - "remove-principals": RemovePrincipals, + Create.String(): Create, + List.String(): List, + Update.String(): Update, + Read.String(): Read, + Delete.String(): Delete, + Authenticate.String(): Authenticate, + All.String(): All, + Connect.String(): Connect, + AddGrants.String(): AddGrants, + RemoveGrants.String(): RemoveGrants, + SetGrants.String(): SetGrants, + AddPrincipals.String(): AddPrincipals, + SetPrincipals.String(): SetPrincipals, + RemovePrincipals.String(): RemovePrincipals, + Deauthenticate.String(): Deauthenticate, } func (a Type) String() string { @@ -58,5 +59,6 @@ func (a Type) String() string { "add-principals", "set-principals", "remove-principals", + "deauthenticate", }[a] } diff --git a/internal/types/action/action_test.go b/internal/types/action/action_test.go index ab8d81125..bfaf59d14 100644 --- a/internal/types/action/action_test.go +++ b/internal/types/action/action_test.go @@ -71,6 +71,10 @@ func TestAction(t *testing.T) { action: SetPrincipals, want: "set-principals", }, + { + action: Deauthenticate, + want: "deauthenticate", + }, } for _, tt := range tests { t.Run(tt.want, func(t *testing.T) { diff --git a/internal/types/resource/resource.go b/internal/types/resource/resource.go index 8b9bd524d..f59c05158 100644 --- a/internal/types/resource/resource.go +++ b/internal/types/resource/resource.go @@ -24,6 +24,9 @@ const ( Host Type = 17 Target Type = 18 Global Type = 19 + + // TODO: remove this after demo + Group Type = 20 ) func (r Type) String() string { @@ -48,50 +51,29 @@ func (r Type) String() string { "host", "target", "global", + "group", }[r] } -func StringToResourceType(s string) Type { - switch s { - case Scope.String(): - return Scope - case User.String(): - return User - case StaticGroup.String(): - return StaticGroup - case Role.String(): - return Role - case Org.String(): - return Org - case StaticGroupMember.String(): - return StaticGroupMember - case StaticGroupUserMember.String(): - return StaticGroupUserMember - case AssignedRole.String(): - return AssignedRole - case AssignedUserRole.String(): - return AssignedUserRole - case AssignedStaticGroupRole.String(): - return AssignedStaticGroupRole - case RoleGrant.String(): - return RoleGrant - case AuthMethod.String(): - return AuthMethod - case Project.String(): - return Project - case All.String(): - return All - case HostCatalog.String(): - return HostCatalog - case HostSet.String(): - return HostSet - case Host.String(): - return Host - case Target.String(): - return Target - case Global.String(): - return Global - default: - return Unknown - } +var Map = map[string]Type{ + Scope.String(): Scope, + User.String(): User, + StaticGroup.String(): StaticGroup, + Role.String(): Role, + Org.String(): Org, + StaticGroupMember.String(): StaticGroupMember, + StaticGroupUserMember.String(): StaticGroupUserMember, + AssignedRole.String(): AssignedRole, + AssignedUserRole.String(): AssignedUserRole, + AssignedStaticGroupRole.String(): AssignedStaticGroupRole, + RoleGrant.String(): RoleGrant, + AuthMethod.String(): AuthMethod, + Project.String(): Project, + All.String(): All, + HostCatalog.String(): HostCatalog, + HostSet.String(): HostSet, + Host.String(): Host, + Target.String(): Target, + Global.String(): Global, + Group.String(): Group, } diff --git a/internal/types/resource/resource_test.go b/internal/types/resource/resource_test.go index 2886ac9f5..3402c0aed 100644 --- a/internal/types/resource/resource_test.go +++ b/internal/types/resource/resource_test.go @@ -95,7 +95,7 @@ func Test_Resource(t *testing.T) { } for _, tt := range tests { t.Run(tt.typeString, func(t *testing.T) { - assert.Equalf(t, tt.want, StringToResourceType(tt.typeString), "unexpected type for %s", tt.typeString) + assert.Equalf(t, tt.want, Map[tt.typeString], "unexpected type for %s", tt.typeString) assert.Equalf(t, tt.typeString, tt.want.String(), "unexpected string for %s", tt.typeString) }) } diff --git a/internal/types/scope/scope.go b/internal/types/scope/scope.go index c07ff5688..deed743c8 100644 --- a/internal/types/scope/scope.go +++ b/internal/types/scope/scope.go @@ -28,15 +28,8 @@ func (s Type) Prefix() string { }[s] } -func StringToScopeType(s string) Type { - switch s { - case Global.String(): - return Global - case Org.String(): - return Org - case Project.String(): - return Project - default: - return Unknown - } +var Map = map[string]Type{ + Global.String(): Global, + Org.String(): Org, + Project.String(): Project, } diff --git a/internal/types/scope/scope_test.go b/internal/types/scope/scope_test.go index cf7368990..8e97f4af4 100644 --- a/internal/types/scope/scope_test.go +++ b/internal/types/scope/scope_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_StringToScopeType(t *testing.T) { +func Test_Map(t *testing.T) { tests := []struct { name string s string @@ -41,7 +41,7 @@ func Test_StringToScopeType(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert := assert.New(t) - got := StringToScopeType(tt.s) + got := Map[tt.s] assert.Equal(tt.want, got) assert.Equalf(tt.wantPrefix, got.Prefix(), "unexpected prefix for %s", tt.s) })