Satellite api keys api (#936)

This commit is contained in:
Yaroslav Vorobiov 2018-12-27 17:30:15 +02:00 committed by GitHub
parent 472341da23
commit d65cefcac7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 384 additions and 143 deletions

View File

@ -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
}

View File

@ -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.

View File

@ -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
}

View File

@ -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 = ?
)

View File

@ -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)

View File

@ -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.

View File

@ -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)
})
}

View 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
}

View File

@ -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
},
},
},
})
}

View File

@ -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)
},
},
},
})
}

View File

@ -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

View File

@ -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)
}