Satellite api keys api (#936)
This commit is contained in:
parent
472341da23
commit
d65cefcac7
@ -4,35 +4,72 @@
|
||||
package satellite
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"github.com/skyrings/skyring-common/tools/uuid"
|
||||
)
|
||||
|
||||
// APIKeys is interface for working with apikeys store
|
||||
// APIKeys is interface for working with api keys store
|
||||
type APIKeys interface {
|
||||
// GetByProjectID retrieves list of APIKeys for given projectID
|
||||
GetByProjectID(ctx context.Context, projectID uuid.UUID) ([]APIKey, error)
|
||||
// Get retrieves APIKey with given ID
|
||||
Get(ctx context.Context, id uuid.UUID) (*APIKey, error)
|
||||
// Create creates and stores new APIKey
|
||||
Create(ctx context.Context, key APIKey) (*APIKey, error)
|
||||
// Update updates APIKey in store
|
||||
Update(ctx context.Context, key APIKey) error
|
||||
// Delete deletes APIKey from store
|
||||
GetByProjectID(ctx context.Context, projectID uuid.UUID) ([]APIKeyInfo, error)
|
||||
// Get retrieves APIKeyInfo with given ID
|
||||
Get(ctx context.Context, id uuid.UUID) (*APIKeyInfo, error)
|
||||
// Create creates and stores new APIKeyInfo
|
||||
Create(ctx context.Context, key APIKey, info APIKeyInfo) (*APIKeyInfo, error)
|
||||
// Update updates APIKeyInfo in store
|
||||
Update(ctx context.Context, key APIKeyInfo) error
|
||||
// Delete deletes APIKeyInfo from store
|
||||
Delete(ctx context.Context, id uuid.UUID) error
|
||||
}
|
||||
|
||||
// APIKey describing apikey model in the database
|
||||
type APIKey struct {
|
||||
// APIKeyInfo describing api key model in the database
|
||||
type APIKeyInfo struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
|
||||
// Fk on project
|
||||
ProjectID uuid.UUID `json:"projectId"`
|
||||
|
||||
Key []byte `json:"key"`
|
||||
Name string `json:"name"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
// APIKey is an api key type
|
||||
type APIKey [24]byte
|
||||
|
||||
// String implements Stringer
|
||||
func (key APIKey) String() string {
|
||||
emptyKey := APIKey{}
|
||||
if bytes.Equal(key[:], emptyKey[:]) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return base64.URLEncoding.EncodeToString(key[:])
|
||||
}
|
||||
|
||||
// APIKeyFromBytes creates new key from byte slice
|
||||
func APIKeyFromBytes(b []byte) *APIKey {
|
||||
key := new(APIKey)
|
||||
copy(key[:], b)
|
||||
return key
|
||||
}
|
||||
|
||||
// createAPIKey creates new api key
|
||||
func createAPIKey() (*APIKey, error) {
|
||||
key := new(APIKey)
|
||||
|
||||
n, err := io.ReadFull(rand.Reader, key[:])
|
||||
if err != nil || n != 24 {
|
||||
return nil, errs.New("error creating api key")
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ import (
|
||||
|
||||
// ProjectMembers exposes methods to manage ProjectMembers table in database.
|
||||
type ProjectMembers interface {
|
||||
// GetByMemberID is a method for querying project member from the database by memberID.
|
||||
GetByMemberID(ctx context.Context, memberID uuid.UUID) (*ProjectMember, error)
|
||||
// GetByMemberID is a method for querying project members from the database by memberID.
|
||||
GetByMemberID(ctx context.Context, memberID uuid.UUID) ([]ProjectMember, error)
|
||||
// GetByProjectID is a method for querying project members from the database by projectID, offset and limit.
|
||||
GetByProjectID(ctx context.Context, projectID uuid.UUID, limit int, offset int64) ([]ProjectMember, error)
|
||||
// Insert is a method for inserting project member into the database.
|
||||
|
@ -19,23 +19,23 @@ type apikeys struct {
|
||||
}
|
||||
|
||||
// GetByProjectID implements satellite.APIKeys ordered by name
|
||||
func (keys *apikeys) GetByProjectID(ctx context.Context, projectID uuid.UUID) ([]satellite.APIKey, error) {
|
||||
func (keys *apikeys) GetByProjectID(ctx context.Context, projectID uuid.UUID) ([]satellite.APIKeyInfo, error) {
|
||||
dbKeys, err := keys.db.All_ApiKey_By_ProjectId_OrderBy_Asc_Name(ctx, dbx.ApiKey_ProjectId(projectID[:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var apiKeys []satellite.APIKey
|
||||
var apiKeys []satellite.APIKeyInfo
|
||||
var parseErr errs.Group
|
||||
|
||||
for _, key := range dbKeys {
|
||||
apiKey, err := toAPIKey(key)
|
||||
info, err := fromDBXAPIKey(key)
|
||||
if err != nil {
|
||||
parseErr.Add(err)
|
||||
continue
|
||||
}
|
||||
|
||||
apiKeys = append(apiKeys, *apiKey)
|
||||
apiKeys = append(apiKeys, *info)
|
||||
}
|
||||
|
||||
if err := parseErr.Err(); err != nil {
|
||||
@ -46,17 +46,17 @@ func (keys *apikeys) GetByProjectID(ctx context.Context, projectID uuid.UUID) ([
|
||||
}
|
||||
|
||||
// Get implements satellite.APIKeys
|
||||
func (keys *apikeys) Get(ctx context.Context, id uuid.UUID) (*satellite.APIKey, error) {
|
||||
func (keys *apikeys) Get(ctx context.Context, id uuid.UUID) (*satellite.APIKeyInfo, error) {
|
||||
dbKey, err := keys.db.Get_ApiKey_By_Id(ctx, dbx.ApiKey_Id(id[:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toAPIKey(dbKey)
|
||||
return fromDBXAPIKey(dbKey)
|
||||
}
|
||||
|
||||
// Create implements satellite.APIKeys
|
||||
func (keys *apikeys) Create(ctx context.Context, key satellite.APIKey) (*satellite.APIKey, error) {
|
||||
func (keys *apikeys) Create(ctx context.Context, key satellite.APIKey, info satellite.APIKeyInfo) (*satellite.APIKeyInfo, error) {
|
||||
id, err := uuid.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -65,20 +65,20 @@ func (keys *apikeys) Create(ctx context.Context, key satellite.APIKey) (*satelli
|
||||
dbKey, err := keys.db.Create_ApiKey(
|
||||
ctx,
|
||||
dbx.ApiKey_Id(id[:]),
|
||||
dbx.ApiKey_ProjectId(key.ProjectID[:]),
|
||||
dbx.ApiKey_Key(key.Key),
|
||||
dbx.ApiKey_Name(key.Name),
|
||||
dbx.ApiKey_ProjectId(info.ProjectID[:]),
|
||||
dbx.ApiKey_Key(key[:]),
|
||||
dbx.ApiKey_Name(info.Name),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toAPIKey(dbKey)
|
||||
return fromDBXAPIKey(dbKey)
|
||||
}
|
||||
|
||||
// Update implements satellite.APIKeys
|
||||
func (keys *apikeys) Update(ctx context.Context, key satellite.APIKey) error {
|
||||
func (keys *apikeys) Update(ctx context.Context, key satellite.APIKeyInfo) error {
|
||||
_, err := keys.db.Update_ApiKey_By_Id(
|
||||
ctx,
|
||||
dbx.ApiKey_Id(key.ID[:]),
|
||||
@ -96,8 +96,8 @@ func (keys *apikeys) Delete(ctx context.Context, id uuid.UUID) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// toAPIKey converts dbx.ApiKey to satellite.APIKey
|
||||
func toAPIKey(key *dbx.ApiKey) (*satellite.APIKey, error) {
|
||||
// fromDBXAPIKey converts dbx.ApiKey to satellite.APIKeyInfo
|
||||
func fromDBXAPIKey(key *dbx.ApiKey) (*satellite.APIKeyInfo, error) {
|
||||
id, err := bytesToUUID(key.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -108,11 +108,10 @@ func toAPIKey(key *dbx.ApiKey) (*satellite.APIKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &satellite.APIKey{
|
||||
return &satellite.APIKeyInfo{
|
||||
ID: id,
|
||||
ProjectID: projectID,
|
||||
Name: key.Name,
|
||||
Key: key.Key,
|
||||
CreatedAt: key.CreatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
@ -62,10 +62,6 @@ model project_member (
|
||||
)
|
||||
|
||||
read all (
|
||||
select project_member
|
||||
where project_member.project_id = ?
|
||||
)
|
||||
read one (
|
||||
select project_member
|
||||
where project_member.member_id = ?
|
||||
)
|
||||
|
@ -1200,14 +1200,14 @@ func (obj *sqlite3Impl) All_Project_By_ProjectMember_MemberId(ctx context.Contex
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) All_ProjectMember_By_ProjectId(ctx context.Context,
|
||||
project_member_project_id ProjectMember_ProjectId_Field) (
|
||||
func (obj *sqlite3Impl) All_ProjectMember_By_MemberId(ctx context.Context,
|
||||
project_member_member_id ProjectMember_MemberId_Field) (
|
||||
rows []*ProjectMember, err error) {
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("SELECT project_members.member_id, project_members.project_id, project_members.created_at FROM project_members WHERE project_members.project_id = ?")
|
||||
var __embed_stmt = __sqlbundle_Literal("SELECT project_members.member_id, project_members.project_id, project_members.created_at FROM project_members WHERE project_members.member_id = ?")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, project_member_project_id.value())
|
||||
__values = append(__values, project_member_member_id.value())
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
@ -1233,49 +1233,6 @@ func (obj *sqlite3Impl) All_ProjectMember_By_ProjectId(ctx context.Context,
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Get_ProjectMember_By_MemberId(ctx context.Context,
|
||||
project_member_member_id ProjectMember_MemberId_Field) (
|
||||
project_member *ProjectMember, err error) {
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("SELECT project_members.member_id, project_members.project_id, project_members.created_at FROM project_members WHERE project_members.member_id = ? LIMIT 2")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, project_member_member_id.value())
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
__rows, err := obj.driver.Query(__stmt, __values...)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
defer __rows.Close()
|
||||
|
||||
if !__rows.Next() {
|
||||
if err := __rows.Err(); err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
return nil, makeErr(sql.ErrNoRows)
|
||||
}
|
||||
|
||||
project_member = &ProjectMember{}
|
||||
err = __rows.Scan(&project_member.MemberId, &project_member.ProjectId, &project_member.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
|
||||
if __rows.Next() {
|
||||
return nil, tooManyRows("ProjectMember_By_MemberId")
|
||||
}
|
||||
|
||||
if err := __rows.Err(); err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
|
||||
return project_member, nil
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Limited_ProjectMember_By_ProjectId(ctx context.Context,
|
||||
project_member_project_id ProjectMember_ProjectId_Field,
|
||||
limit int, offset int64) (
|
||||
@ -1837,14 +1794,14 @@ func (rx *Rx) All_Project(ctx context.Context) (
|
||||
return tx.All_Project(ctx)
|
||||
}
|
||||
|
||||
func (rx *Rx) All_ProjectMember_By_ProjectId(ctx context.Context,
|
||||
project_member_project_id ProjectMember_ProjectId_Field) (
|
||||
func (rx *Rx) All_ProjectMember_By_MemberId(ctx context.Context,
|
||||
project_member_member_id ProjectMember_MemberId_Field) (
|
||||
rows []*ProjectMember, err error) {
|
||||
var tx *Tx
|
||||
if tx, err = rx.getTx(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
return tx.All_ProjectMember_By_ProjectId(ctx, project_member_project_id)
|
||||
return tx.All_ProjectMember_By_MemberId(ctx, project_member_member_id)
|
||||
}
|
||||
|
||||
func (rx *Rx) All_Project_By_ProjectMember_MemberId(ctx context.Context,
|
||||
@ -1963,16 +1920,6 @@ func (rx *Rx) Get_ApiKey_By_Id(ctx context.Context,
|
||||
return tx.Get_ApiKey_By_Id(ctx, api_key_id)
|
||||
}
|
||||
|
||||
func (rx *Rx) Get_ProjectMember_By_MemberId(ctx context.Context,
|
||||
project_member_member_id ProjectMember_MemberId_Field) (
|
||||
project_member *ProjectMember, err error) {
|
||||
var tx *Tx
|
||||
if tx, err = rx.getTx(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
return tx.Get_ProjectMember_By_MemberId(ctx, project_member_member_id)
|
||||
}
|
||||
|
||||
func (rx *Rx) Get_Project_By_Id(ctx context.Context,
|
||||
project_id Project_Id_Field) (
|
||||
project *Project, err error) {
|
||||
@ -2055,8 +2002,8 @@ type Methods interface {
|
||||
All_Project(ctx context.Context) (
|
||||
rows []*Project, err error)
|
||||
|
||||
All_ProjectMember_By_ProjectId(ctx context.Context,
|
||||
project_member_project_id ProjectMember_ProjectId_Field) (
|
||||
All_ProjectMember_By_MemberId(ctx context.Context,
|
||||
project_member_member_id ProjectMember_MemberId_Field) (
|
||||
rows []*ProjectMember, err error)
|
||||
|
||||
All_Project_By_ProjectMember_MemberId(ctx context.Context,
|
||||
@ -2111,10 +2058,6 @@ type Methods interface {
|
||||
api_key_id ApiKey_Id_Field) (
|
||||
api_key *ApiKey, err error)
|
||||
|
||||
Get_ProjectMember_By_MemberId(ctx context.Context,
|
||||
project_member_member_id ProjectMember_MemberId_Field) (
|
||||
project_member *ProjectMember, err error)
|
||||
|
||||
Get_Project_By_Id(ctx context.Context,
|
||||
project_id Project_Id_Field) (
|
||||
project *Project, err error)
|
||||
|
@ -20,13 +20,13 @@ type projectMembers struct {
|
||||
}
|
||||
|
||||
// GetByMemberID is a method for querying project member from the database by memberID.
|
||||
func (pm *projectMembers) GetByMemberID(ctx context.Context, memberID uuid.UUID) (*satellite.ProjectMember, error) {
|
||||
projectMemberDbx, err := pm.db.Get_ProjectMember_By_MemberId(ctx, dbx.ProjectMember_MemberId(memberID[:]))
|
||||
func (pm *projectMembers) GetByMemberID(ctx context.Context, memberID uuid.UUID) ([]satellite.ProjectMember, error) {
|
||||
projectMembersDbx, err := pm.db.All_ProjectMember_By_MemberId(ctx, dbx.ProjectMember_MemberId(memberID[:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return projectMemberFromDBX(projectMemberDbx)
|
||||
return projectMembersFromDbxSlice(projectMembersDbx)
|
||||
}
|
||||
|
||||
// GetByProjectID is a method for querying project members from the database by projectID, offset and limit.
|
||||
|
@ -97,20 +97,20 @@ func TestProjectMembersRepository(t *testing.T) {
|
||||
|
||||
t.Run("Get member by memberID success", func(t *testing.T) {
|
||||
originalMember1 := createdUsers[0]
|
||||
selectedMember1, err := projectMembers.GetByMemberID(ctx, originalMember1.ID)
|
||||
selectedMembers1, err := projectMembers.GetByMemberID(ctx, originalMember1.ID)
|
||||
|
||||
assert.NotNil(t, selectedMember1)
|
||||
assert.NotNil(t, selectedMembers1)
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, originalMember1.ID, selectedMember1.MemberID)
|
||||
assert.Equal(t, originalMember1.ID, selectedMembers1[0].MemberID)
|
||||
|
||||
originalMember2 := createdUsers[1]
|
||||
selectedMember2, err := projectMembers.GetByMemberID(ctx, originalMember2.ID)
|
||||
selectedMembers2, err := projectMembers.GetByMemberID(ctx, originalMember2.ID)
|
||||
|
||||
assert.NotNil(t, selectedMember2)
|
||||
assert.NotNil(t, selectedMembers2)
|
||||
assert.Nil(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, originalMember2.ID, selectedMember2.MemberID)
|
||||
assert.Equal(t, originalMember2.ID, selectedMembers2[0].MemberID)
|
||||
})
|
||||
}
|
||||
|
||||
|
59
pkg/satellite/satelliteweb/satelliteql/apikey.go
Normal file
59
pkg/satellite/satelliteweb/satelliteql/apikey.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package satelliteql
|
||||
|
||||
import (
|
||||
"github.com/graphql-go/graphql"
|
||||
|
||||
"storj.io/storj/pkg/satellite"
|
||||
)
|
||||
|
||||
const (
|
||||
apiKeyInfoType = "keyInfo"
|
||||
createAPIKeyType = "graphqlCreateAPIKey"
|
||||
|
||||
fieldKey = "key"
|
||||
)
|
||||
|
||||
// graphqlAPIKeyInfo creates satellite.APIKeyInfo graphql object
|
||||
func graphqlAPIKeyInfo() *graphql.Object {
|
||||
return graphql.NewObject(graphql.ObjectConfig{
|
||||
Name: apiKeyInfoType,
|
||||
Fields: graphql.Fields{
|
||||
fieldID: &graphql.Field{
|
||||
Type: graphql.String,
|
||||
},
|
||||
fieldProjectID: &graphql.Field{
|
||||
Type: graphql.String,
|
||||
},
|
||||
fieldName: &graphql.Field{
|
||||
Type: graphql.String,
|
||||
},
|
||||
fieldCreatedAt: &graphql.Field{
|
||||
Type: graphql.DateTime,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// graphqlCreateAPIKey creates createAPIKey graphql object
|
||||
func graphqlCreateAPIKey(types Types) *graphql.Object {
|
||||
return graphql.NewObject(graphql.ObjectConfig{
|
||||
Name: createAPIKeyType,
|
||||
Fields: graphql.Fields{
|
||||
fieldKey: &graphql.Field{
|
||||
Type: graphql.String,
|
||||
},
|
||||
apiKeyInfoType: &graphql.Field{
|
||||
Type: types.APIKeyInfo(),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// createAPIKey holds satellite.APIKey and satellite.APIKeyInfo
|
||||
type createAPIKey struct {
|
||||
Key *satellite.APIKey
|
||||
KeyInfo *satellite.APIKeyInfo
|
||||
}
|
@ -23,8 +23,11 @@ const (
|
||||
deleteProjectMutation = "deleteProject"
|
||||
updateProjectDescriptionMutation = "updateProjectDescription"
|
||||
|
||||
addProjectMemberMutation = "addProjectMembers"
|
||||
deleteProjectMemberMutation = "deleteProjectMembers"
|
||||
addProjectMembersMutation = "addProjectMembers"
|
||||
deleteProjectMembersMutation = "deleteProjectMembers"
|
||||
|
||||
createAPIKeyMutation = "createAPIKey"
|
||||
deleteAPIKeyMutation = "deleteAPIKey"
|
||||
|
||||
input = "input"
|
||||
|
||||
@ -189,7 +192,7 @@ func rootMutation(service *satellite.Service, types Types) *graphql.Object {
|
||||
},
|
||||
},
|
||||
// add user as member of given project
|
||||
addProjectMemberMutation: &graphql.Field{
|
||||
addProjectMembersMutation: &graphql.Field{
|
||||
Type: types.Project(),
|
||||
Args: graphql.FieldConfigArgument{
|
||||
fieldProjectID: &graphql.ArgumentConfig{
|
||||
@ -222,7 +225,7 @@ func rootMutation(service *satellite.Service, types Types) *graphql.Object {
|
||||
},
|
||||
},
|
||||
// delete user membership for given project
|
||||
deleteProjectMemberMutation: &graphql.Field{
|
||||
deleteProjectMembersMutation: &graphql.Field{
|
||||
Type: types.Project(),
|
||||
Args: graphql.FieldConfigArgument{
|
||||
fieldProjectID: &graphql.ArgumentConfig{
|
||||
@ -254,6 +257,66 @@ func rootMutation(service *satellite.Service, types Types) *graphql.Object {
|
||||
return service.GetProject(p.Context, *projectID)
|
||||
},
|
||||
},
|
||||
// creates new api key
|
||||
createAPIKeyMutation: &graphql.Field{
|
||||
Type: types.CreateAPIKey(),
|
||||
Args: graphql.FieldConfigArgument{
|
||||
fieldProjectID: &graphql.ArgumentConfig{
|
||||
Type: graphql.NewNonNull(graphql.String),
|
||||
},
|
||||
fieldName: &graphql.ArgumentConfig{
|
||||
Type: graphql.NewNonNull(graphql.String),
|
||||
},
|
||||
},
|
||||
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
||||
projectID, _ := p.Args[fieldProjectID].(string)
|
||||
name, _ := p.Args[fieldName].(string)
|
||||
|
||||
pID, err := uuid.Parse(projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, key, err := service.CreateAPIKey(p.Context, *pID, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createAPIKey{
|
||||
Key: key,
|
||||
KeyInfo: info,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
// deletes api key
|
||||
deleteAPIKeyMutation: &graphql.Field{
|
||||
Type: types.APIKeyInfo(),
|
||||
Args: graphql.FieldConfigArgument{
|
||||
fieldID: &graphql.ArgumentConfig{
|
||||
Type: graphql.NewNonNull(graphql.String),
|
||||
},
|
||||
},
|
||||
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
||||
keyID, _ := p.Args[fieldID].(string)
|
||||
|
||||
id, err := uuid.Parse(keyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := service.GetAPIKeyInfo(p.Context, *id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = service.DeleteAPIKey(p.Context, *id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return key, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ const (
|
||||
// Used in input model
|
||||
fieldIsTermsAccepted = "isTermsAccepted"
|
||||
fieldMembers = "members"
|
||||
fieldAPIKeys = "apiKeys"
|
||||
|
||||
limit = "limit"
|
||||
offset = "offset"
|
||||
@ -80,6 +81,14 @@ func graphqlProject(service *satellite.Service, types Types) *graphql.Object {
|
||||
return users, nil
|
||||
},
|
||||
},
|
||||
fieldAPIKeys: &graphql.Field{
|
||||
Type: graphql.NewList(types.APIKeyInfo()),
|
||||
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
||||
project, _ := p.Source.(*satellite.Project)
|
||||
|
||||
return service.GetAPIKeysInfoByProjectID(p.Context, project.ID)
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ type Types interface {
|
||||
User() *graphql.Object
|
||||
Project() *graphql.Object
|
||||
ProjectMember() *graphql.Object
|
||||
APIKeyInfo() *graphql.Object
|
||||
CreateAPIKey() *graphql.Object
|
||||
|
||||
UserInput() *graphql.InputObject
|
||||
ProjectInput() *graphql.InputObject
|
||||
@ -34,6 +36,8 @@ type TypeCreator struct {
|
||||
user *graphql.Object
|
||||
project *graphql.Object
|
||||
projectMember *graphql.Object
|
||||
apiKeyInfo *graphql.Object
|
||||
createAPIKey *graphql.Object
|
||||
|
||||
userInput *graphql.InputObject
|
||||
projectInput *graphql.InputObject
|
||||
@ -58,6 +62,16 @@ func (c *TypeCreator) Create(service *satellite.Service) error {
|
||||
return err
|
||||
}
|
||||
|
||||
c.apiKeyInfo = graphqlAPIKeyInfo()
|
||||
if err := c.apiKeyInfo.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.createAPIKey = graphqlCreateAPIKey(c)
|
||||
if err := c.createAPIKey.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.projectMember = graphqlProjectMember(service, c)
|
||||
if err := c.projectMember.Error(); err != nil {
|
||||
return err
|
||||
@ -107,6 +121,17 @@ func (c *TypeCreator) User() *graphql.Object {
|
||||
return c.user
|
||||
}
|
||||
|
||||
// APIKeyInfo returns instance of satellite.APIKeyInfo *graphql.Object
|
||||
func (c *TypeCreator) APIKeyInfo() *graphql.Object {
|
||||
return c.apiKeyInfo
|
||||
}
|
||||
|
||||
// CreateAPIKey encapsulates api key and key info
|
||||
// returns *graphql.Object
|
||||
func (c *TypeCreator) CreateAPIKey() *graphql.Object {
|
||||
return c.createAPIKey
|
||||
}
|
||||
|
||||
// Project returns instance of satellite.Project *graphql.Object
|
||||
func (c *TypeCreator) Project() *graphql.Object {
|
||||
return c.project
|
||||
|
@ -5,9 +5,7 @@ package satellite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@ -285,11 +283,15 @@ func (s *Service) UpdateProject(ctx context.Context, projectID uuid.UUID, descri
|
||||
// AddProjectMembers adds users by email to given project
|
||||
func (s *Service) AddProjectMembers(ctx context.Context, projectID uuid.UUID, emails []string) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
_, err = GetAuth(ctx)
|
||||
auth, err := GetAuth(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = s.isProjectMember(ctx, auth.User.ID, projectID); err != nil {
|
||||
return ErrUnauthorized.Wrap(err)
|
||||
}
|
||||
|
||||
var userIDs []uuid.UUID
|
||||
var userErr errs.Group
|
||||
|
||||
@ -305,7 +307,7 @@ func (s *Service) AddProjectMembers(ctx context.Context, projectID uuid.UUID, em
|
||||
userIDs = append(userIDs, user.ID)
|
||||
}
|
||||
|
||||
if err := userErr.Err(); err != nil {
|
||||
if err = userErr.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -315,25 +317,38 @@ func (s *Service) AddProjectMembers(ctx context.Context, projectID uuid.UUID, em
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = errs.Combine(err, tx.Rollback())
|
||||
return
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
}()
|
||||
|
||||
for _, uID := range userIDs {
|
||||
_, err := tx.ProjectMembers().Insert(ctx, uID, projectID)
|
||||
_, err = tx.ProjectMembers().Insert(ctx, uID, projectID)
|
||||
|
||||
if err != nil {
|
||||
return errs.Combine(err, tx.Rollback())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteProjectMembers removes users by email from given project
|
||||
func (s *Service) DeleteProjectMembers(ctx context.Context, projectID uuid.UUID, emails []string) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
_, err = GetAuth(ctx)
|
||||
auth, err := GetAuth(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = s.isProjectMember(ctx, auth.User.ID, projectID); err != nil {
|
||||
return ErrUnauthorized.Wrap(err)
|
||||
}
|
||||
|
||||
var userIDs []uuid.UUID
|
||||
var userErr errs.Group
|
||||
|
||||
@ -349,7 +364,7 @@ func (s *Service) DeleteProjectMembers(ctx context.Context, projectID uuid.UUID,
|
||||
userIDs = append(userIDs, user.ID)
|
||||
}
|
||||
|
||||
if err := userErr.Err(); err != nil {
|
||||
if err = userErr.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -359,15 +374,24 @@ func (s *Service) DeleteProjectMembers(ctx context.Context, projectID uuid.UUID,
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = errs.Combine(err, tx.Rollback())
|
||||
return
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
}()
|
||||
|
||||
for _, uID := range userIDs {
|
||||
err := tx.ProjectMembers().Delete(ctx, uID, projectID)
|
||||
err = tx.ProjectMembers().Delete(ctx, uID, projectID)
|
||||
|
||||
if err != nil {
|
||||
return errs.Combine(err, tx.Rollback())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetProjectMembers returns ProjectMembers for given Project
|
||||
@ -389,37 +413,91 @@ func (s *Service) GetProjectMembers(ctx context.Context, projectID uuid.UUID, li
|
||||
return s.store.ProjectMembers().GetByProjectID(ctx, projectID, limit, offset)
|
||||
}
|
||||
|
||||
// apiKey is a mock api key type
|
||||
type apiKey [24]byte
|
||||
// CreateAPIKey creates new api key
|
||||
func (s *Service) CreateAPIKey(ctx context.Context, projectID uuid.UUID, name string) (*APIKeyInfo, *APIKey, error) {
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
// createAPIKey creates new mock api key
|
||||
func createAPIKey() (*apiKey, error) {
|
||||
key := new(apiKey)
|
||||
n, err := io.ReadFull(rand.Reader, key[:])
|
||||
if err != nil || n != 24 {
|
||||
return nil, errs.New("error creating apikey")
|
||||
auth, err := GetAuth(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
_, err = s.isProjectMember(ctx, auth.User.ID, projectID)
|
||||
if err != nil {
|
||||
return nil, nil, ErrUnauthorized.Wrap(err)
|
||||
}
|
||||
|
||||
key, err := createAPIKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
info, err := s.store.APIKeys().Create(ctx, *key, APIKeyInfo{
|
||||
Name: name,
|
||||
ProjectID: projectID,
|
||||
})
|
||||
return info, key, err
|
||||
}
|
||||
|
||||
// GetAPIKeyInfo retrieves api key by id
|
||||
func (s *Service) GetAPIKeyInfo(ctx context.Context, id uuid.UUID) (*APIKeyInfo, error) {
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
auth, err := GetAuth(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := s.store.APIKeys().Get(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = s.isProjectMember(ctx, auth.User.ID, key.ProjectID)
|
||||
if err != nil {
|
||||
return nil, ErrUnauthorized.Wrap(err)
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// CreateAPIKey creates new api key
|
||||
func (s *Service) CreateAPIKey(ctx context.Context, projectID uuid.UUID, name string) (*APIKey, error) {
|
||||
_, err := GetAuth(ctx)
|
||||
// DeleteAPIKey deletes api key by id
|
||||
func (s *Service) DeleteAPIKey(ctx context.Context, id uuid.UUID) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
auth, err := GetAuth(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key, err := s.store.APIKeys().Get(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.isProjectMember(ctx, auth.User.ID, key.ProjectID)
|
||||
if err != nil {
|
||||
return ErrUnauthorized.Wrap(err)
|
||||
}
|
||||
|
||||
return s.store.APIKeys().Delete(ctx, id)
|
||||
}
|
||||
|
||||
// GetAPIKeysInfoByProjectID retrieves all api keys for a given project
|
||||
func (s *Service) GetAPIKeysInfoByProjectID(ctx context.Context, projectID uuid.UUID) (info []APIKeyInfo, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
auth, err := GetAuth(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := createAPIKey()
|
||||
_, err = s.isProjectMember(ctx, auth.User.ID, projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, ErrUnauthorized.Wrap(err)
|
||||
}
|
||||
|
||||
return s.store.APIKeys().Create(ctx, APIKey{
|
||||
Name: name,
|
||||
Key: key[:],
|
||||
ProjectID: projectID,
|
||||
})
|
||||
return s.store.APIKeys().GetByProjectID(ctx, projectID)
|
||||
}
|
||||
|
||||
// Authorize validates token from context and returns authorized Authorization
|
||||
@ -501,3 +579,35 @@ func (s *Service) authorize(ctx context.Context, claims *satelliteauth.Claims) (
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// isProjectMember is return type of isProjectMember service method
|
||||
type isProjectMember struct {
|
||||
project *Project
|
||||
membership *ProjectMember
|
||||
}
|
||||
|
||||
// ErrNoMembership is error type of not belonging to a specific project
|
||||
var ErrNoMembership = errs.Class("no membership error")
|
||||
|
||||
// isProjectMember checks if the user is a member of given project
|
||||
func (s *Service) isProjectMember(ctx context.Context, userID uuid.UUID, projectID uuid.UUID) (result isProjectMember, err error) {
|
||||
project, err := s.store.Projects().Get(ctx, projectID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
memberships, err := s.store.ProjectMembers().GetByMemberID(ctx, userID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, membership := range memberships {
|
||||
if membership.ProjectID == projectID {
|
||||
result.membership = &membership
|
||||
result.project = project
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return isProjectMember{}, ErrNoMembership.New("user % is not a member of project %s", userID, project.ID)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user