2019-02-21 15:08:30 +00:00
// Copyright 2019 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and
// limitations under the License.
package command
import (
"context"
"encoding/json"
2019-03-14 13:36:18 +00:00
"path"
2019-04-29 12:57:07 +00:00
"time"
2019-02-21 15:08:30 +00:00
2019-04-26 14:00:03 +00:00
"github.com/sorintlab/agola/internal/datamanager"
2019-02-21 15:08:30 +00:00
"github.com/sorintlab/agola/internal/db"
"github.com/sorintlab/agola/internal/services/configstore/readdb"
"github.com/sorintlab/agola/internal/services/types"
"github.com/sorintlab/agola/internal/util"
"github.com/pkg/errors"
uuid "github.com/satori/go.uuid"
"go.uber.org/zap"
)
type CommandHandler struct {
log * zap . SugaredLogger
readDB * readdb . ReadDB
2019-04-26 14:00:03 +00:00
dm * datamanager . DataManager
2019-02-21 15:08:30 +00:00
}
2019-04-26 14:00:03 +00:00
func NewCommandHandler ( logger * zap . Logger , readDB * readdb . ReadDB , dm * datamanager . DataManager ) * CommandHandler {
2019-02-21 15:08:30 +00:00
return & CommandHandler {
log : logger . Sugar ( ) ,
readDB : readDB ,
2019-04-26 14:00:03 +00:00
dm : dm ,
2019-02-21 15:08:30 +00:00
}
}
2019-03-14 13:36:18 +00:00
func ( s * CommandHandler ) CreateProjectGroup ( ctx context . Context , projectGroup * types . ProjectGroup ) ( * types . ProjectGroup , error ) {
if projectGroup . Name == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "project group name required" ) )
}
2019-05-03 10:41:49 +00:00
if ! util . ValidateName ( projectGroup . Name ) {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid project group name %q" , projectGroup . Name ) )
}
2019-03-14 13:36:18 +00:00
if projectGroup . Parent . ID == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "project group parent id required" ) )
}
2019-04-30 15:09:26 +00:00
if ! types . IsValidVisibility ( projectGroup . Visibility ) {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid project group visibility" ) )
}
2019-03-14 13:36:18 +00:00
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-03-14 13:36:18 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-03-14 13:36:18 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
parentProjectGroup , err := s . readDB . GetProjectGroup ( tx , projectGroup . Parent . ID )
if err != nil {
return err
}
if parentProjectGroup == nil {
return util . NewErrBadRequest ( errors . Errorf ( "project group with id %q doesn't exist" , projectGroup . Parent . ID ) )
}
projectGroup . Parent . ID = parentProjectGroup . ID
groupPath , err := s . readDB . GetProjectGroupPath ( tx , parentProjectGroup )
if err != nil {
return err
}
pp := path . Join ( groupPath , projectGroup . Name )
2019-04-29 08:37:23 +00:00
// changegroup is the projectgroup path. Use "projectpath" prefix as it must
// cover both projects and projectgroups
cgNames := [ ] string { util . EncodeSha256Hex ( "projectpath-" + pp ) }
2019-03-14 13:36:18 +00:00
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
// check duplicate project name
p , err := s . readDB . GetProjectByName ( tx , projectGroup . Parent . ID , projectGroup . Name )
if err != nil {
return err
}
if p != nil {
return util . NewErrBadRequest ( errors . Errorf ( "project with name %q, path %q already exists" , p . Name , pp ) )
}
// check duplicate project group name
pg , err := s . readDB . GetProjectGroupByName ( tx , projectGroup . Parent . ID , projectGroup . Name )
if err != nil {
return err
}
if pg != nil {
return util . NewErrBadRequest ( errors . Errorf ( "project group with name %q, path %q already exists" , pg . Name , pp ) )
}
return nil
} )
if err != nil {
return nil , err
}
projectGroup . ID = uuid . NewV4 ( ) . String ( )
projectGroup . Parent . Type = types . ConfigTypeProjectGroup
pcj , err := json . Marshal ( projectGroup )
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal projectGroup" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-03-14 13:36:18 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeProjectGroup ) ,
ID : projectGroup . ID ,
2019-03-14 13:36:18 +00:00
Data : pcj ,
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-03-14 13:36:18 +00:00
return projectGroup , err
}
2019-02-21 15:08:30 +00:00
func ( s * CommandHandler ) CreateProject ( ctx context . Context , project * types . Project ) ( * types . Project , error ) {
if project . Name == "" {
2019-03-12 14:12:19 +00:00
return nil , util . NewErrBadRequest ( errors . Errorf ( "project name required" ) )
2019-02-21 15:08:30 +00:00
}
2019-05-03 10:41:49 +00:00
if ! util . ValidateName ( project . Name ) {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid project name %q" , project . Name ) )
}
2019-03-14 13:36:18 +00:00
if project . Parent . ID == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "project parent id required" ) )
2019-02-28 14:52:35 +00:00
}
2019-05-03 10:41:49 +00:00
if project . Parent . Type != types . ConfigTypeProjectGroup {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid project parent type %q" , project . Parent . Type ) )
}
2019-04-30 15:09:26 +00:00
if ! types . IsValidVisibility ( project . Visibility ) {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid project visibility" ) )
}
2019-05-03 10:21:44 +00:00
if ! types . IsValidRemoteRepositoryConfigType ( project . RemoteRepositoryConfigType ) {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid project remote repository config type %q" , project . RemoteRepositoryConfigType ) )
}
2019-05-03 10:41:49 +00:00
if project . RemoteRepositoryConfigType == types . RemoteRepositoryConfigTypeRemoteSource {
if project . RemoteSourceID == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "empty remote source id" ) )
}
if project . LinkedAccountID == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "empty linked account id" ) )
}
if project . RepositoryID == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "empty remote repository id" ) )
}
if project . RepositoryPath == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "empty remote repository path" ) )
}
}
2019-02-21 15:08:30 +00:00
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-02-21 15:08:30 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-21 15:08:30 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
2019-03-14 13:36:18 +00:00
group , err := s . readDB . GetProjectGroup ( tx , project . Parent . ID )
2019-02-21 15:08:30 +00:00
if err != nil {
return err
}
2019-03-14 13:36:18 +00:00
if group == nil {
return util . NewErrBadRequest ( errors . Errorf ( "project group with id %q doesn't exist" , project . Parent . ID ) )
}
project . Parent . ID = group . ID
2019-02-21 15:08:30 +00:00
2019-03-14 13:36:18 +00:00
groupPath , err := s . readDB . GetProjectGroupPath ( tx , group )
if err != nil {
return err
2019-02-28 14:52:35 +00:00
}
2019-03-14 13:36:18 +00:00
pp := path . Join ( groupPath , project . Name )
2019-04-29 08:37:23 +00:00
// changegroup is the project path. Use "projectpath" prefix as it must
// cover both projects and projectgroups
cgNames := [ ] string { util . EncodeSha256Hex ( "projectpath-" + pp ) }
2019-03-14 13:36:18 +00:00
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
2019-02-21 15:08:30 +00:00
// check duplicate project name
2019-03-14 13:36:18 +00:00
p , err := s . readDB . GetProjectByName ( tx , project . Parent . ID , project . Name )
2019-02-21 15:08:30 +00:00
if err != nil {
return err
}
if p != nil {
2019-03-14 13:36:18 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "project with name %q, path %q already exists" , p . Name , pp ) )
}
// check duplicate project group name
pg , err := s . readDB . GetProjectGroupByName ( tx , project . Parent . ID , project . Name )
if err != nil {
return err
}
if pg != nil {
return util . NewErrBadRequest ( errors . Errorf ( "project group with name %q, path %q already exists" , pg . Name , pp ) )
2019-02-21 15:08:30 +00:00
}
2019-05-03 07:53:38 +00:00
2019-05-03 10:21:44 +00:00
if project . RemoteRepositoryConfigType == types . RemoteRepositoryConfigTypeRemoteSource {
// check that the linked account matches the remote source
user , err := s . readDB . GetUserByLinkedAccount ( tx , project . LinkedAccountID )
if err != nil {
return errors . Wrapf ( err , "failed to get user with linked account id %q" , project . LinkedAccountID )
}
if user == nil {
return util . NewErrBadRequest ( errors . Errorf ( "user for linked account %q doesn't exist" , project . LinkedAccountID ) )
}
la , ok := user . LinkedAccounts [ project . LinkedAccountID ]
if ! ok {
return util . NewErrBadRequest ( errors . Errorf ( "linked account id %q for user %q doesn't exist" , project . LinkedAccountID , user . Name ) )
}
if la . RemoteSourceID != project . RemoteSourceID {
return util . NewErrBadRequest ( errors . Errorf ( "linked account id %q remote source %q different than project remote source %q" , project . LinkedAccountID , la . RemoteSourceID , project . RemoteSourceID ) )
}
2019-05-03 07:53:38 +00:00
}
2019-02-21 15:08:30 +00:00
return nil
} )
if err != nil {
return nil , err
}
project . ID = uuid . NewV4 ( ) . String ( )
2019-03-14 13:36:18 +00:00
project . Parent . Type = types . ConfigTypeProjectGroup
2019-02-21 15:08:30 +00:00
pcj , err := json . Marshal ( project )
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal project" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-21 15:08:30 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeProject ) ,
ID : project . ID ,
2019-02-21 15:08:30 +00:00
Data : pcj ,
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-21 15:08:30 +00:00
return project , err
}
2019-03-14 13:36:18 +00:00
func ( s * CommandHandler ) DeleteProject ( ctx context . Context , projectRef string ) error {
2019-02-21 15:08:30 +00:00
var project * types . Project
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-02-21 15:08:30 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-21 15:08:30 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
// check project existance
2019-04-02 09:06:45 +00:00
project , err = s . readDB . GetProject ( tx , projectRef )
2019-02-21 15:08:30 +00:00
if err != nil {
return err
}
if project == nil {
2019-03-14 13:36:18 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "project %q doesn't exist" , projectRef ) )
}
2019-04-29 08:37:23 +00:00
// changegroup is the project id.
cgNames := [ ] string { util . EncodeSha256Hex ( project . ID ) }
2019-03-14 13:36:18 +00:00
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
2019-02-21 15:08:30 +00:00
}
2019-03-14 13:36:18 +00:00
2019-02-21 15:08:30 +00:00
return nil
} )
if err != nil {
return err
}
2019-04-02 09:06:45 +00:00
// TODO(sgotti) delete project secrets/variables
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-21 15:08:30 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypeDelete ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeProject ) ,
ID : project . ID ,
2019-02-21 15:08:30 +00:00
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-21 15:08:30 +00:00
return err
}
2019-03-29 16:50:51 +00:00
type CreateUserRequest struct {
UserName string
CreateUserLARequest * CreateUserLARequest
}
func ( s * CommandHandler ) CreateUser ( ctx context . Context , req * CreateUserRequest ) ( * types . User , error ) {
if req . UserName == "" {
2019-03-12 14:12:19 +00:00
return nil , util . NewErrBadRequest ( errors . Errorf ( "user name required" ) )
2019-02-21 15:08:30 +00:00
}
2019-05-03 10:41:49 +00:00
if ! util . ValidateName ( req . UserName ) {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid user name %q" , req . UserName ) )
}
2019-02-21 15:08:30 +00:00
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-04-29 08:37:23 +00:00
// changegroup is the username (and in future the email) to ensure no
// concurrent user creation/modification using the same name
cgNames := [ ] string { util . EncodeSha256Hex ( "username-" + req . UserName ) }
2019-03-29 16:50:51 +00:00
var rs * types . RemoteSource
2019-02-21 15:08:30 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-21 15:08:30 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
// check duplicate user name
2019-03-29 16:50:51 +00:00
u , err := s . readDB . GetUserByName ( tx , req . UserName )
2019-02-21 15:08:30 +00:00
if err != nil {
return err
}
if u != nil {
2019-04-30 10:56:43 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "user with name %q already exists" , u . Name ) )
2019-02-21 15:08:30 +00:00
}
2019-03-29 16:50:51 +00:00
if req . CreateUserLARequest != nil {
rs , err = s . readDB . GetRemoteSourceByName ( tx , req . CreateUserLARequest . RemoteSourceName )
if err != nil {
return err
}
if rs == nil {
return util . NewErrBadRequest ( errors . Errorf ( "remote source %q doesn't exist" , req . CreateUserLARequest . RemoteSourceName ) )
}
user , err := s . readDB . GetUserByLinkedAccountRemoteUserIDandSource ( tx , req . CreateUserLARequest . RemoteUserID , rs . ID )
if err != nil {
return errors . Wrapf ( err , "failed to get user for remote user id %q and remote source %q" , req . CreateUserLARequest . RemoteUserID , rs . ID )
}
if user != nil {
return util . NewErrBadRequest ( errors . Errorf ( "user for remote user id %q for remote source %q already exists" , req . CreateUserLARequest . RemoteUserID , req . CreateUserLARequest . RemoteSourceName ) )
}
}
2019-02-21 15:08:30 +00:00
return nil
} )
if err != nil {
return nil , err
}
2019-03-29 16:50:51 +00:00
user := & types . User {
2019-04-30 10:56:43 +00:00
ID : uuid . NewV4 ( ) . String ( ) ,
Name : req . UserName ,
2019-03-29 16:50:51 +00:00
}
if req . CreateUserLARequest != nil {
if user . LinkedAccounts == nil {
user . LinkedAccounts = make ( map [ string ] * types . LinkedAccount )
}
la := & types . LinkedAccount {
2019-04-29 12:57:07 +00:00
ID : uuid . NewV4 ( ) . String ( ) ,
RemoteSourceID : rs . ID ,
RemoteUserID : req . CreateUserLARequest . RemoteUserID ,
RemoteUserName : req . CreateUserLARequest . RemoteUserName ,
UserAccessToken : req . CreateUserLARequest . UserAccessToken ,
Oauth2AccessToken : req . CreateUserLARequest . Oauth2AccessToken ,
Oauth2RefreshToken : req . CreateUserLARequest . Oauth2RefreshToken ,
Oauth2AccessTokenExpiresAt : req . CreateUserLARequest . Oauth2AccessTokenExpiresAt ,
2019-03-29 16:50:51 +00:00
}
user . LinkedAccounts [ la . ID ] = la
}
2019-02-28 14:52:35 +00:00
userj , err := json . Marshal ( user )
2019-02-21 15:08:30 +00:00
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal user" )
}
2019-03-14 13:36:18 +00:00
pg := & types . ProjectGroup {
ID : uuid . NewV4 ( ) . String ( ) ,
Parent : types . Parent {
Type : types . ConfigTypeUser ,
ID : user . ID ,
} ,
}
pgj , err := json . Marshal ( pg )
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal project group" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-21 15:08:30 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeUser ) ,
ID : user . ID ,
2019-02-28 14:52:35 +00:00
Data : userj ,
2019-02-21 15:08:30 +00:00
} ,
2019-03-14 13:36:18 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeProjectGroup ) ,
ID : pg . ID ,
2019-03-14 13:36:18 +00:00
Data : pgj ,
} ,
2019-02-21 15:08:30 +00:00
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-21 15:08:30 +00:00
return user , err
}
2019-05-03 09:07:53 +00:00
func ( s * CommandHandler ) DeleteUser ( ctx context . Context , userRef string ) error {
2019-02-21 15:08:30 +00:00
var user * types . User
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-21 15:08:30 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
// check user existance
2019-05-03 09:07:53 +00:00
user , err = s . readDB . GetUser ( tx , userRef )
2019-02-21 15:08:30 +00:00
if err != nil {
return err
}
if user == nil {
2019-05-03 09:07:53 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "user %q doesn't exist" , userRef ) )
2019-02-21 15:08:30 +00:00
}
2019-04-29 08:37:23 +00:00
// changegroup is the userid
cgNames := [ ] string { util . EncodeSha256Hex ( "userid-" + user . ID ) }
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
2019-02-21 15:08:30 +00:00
return nil
} )
if err != nil {
return err
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-21 15:08:30 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypeDelete ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeUser ) ,
ID : user . ID ,
2019-02-21 15:08:30 +00:00
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-21 15:08:30 +00:00
return err
}
2019-05-03 07:53:38 +00:00
type UpdateUserRequest struct {
2019-05-03 09:07:53 +00:00
UserRef string
2019-05-03 07:53:38 +00:00
UserName string
}
func ( s * CommandHandler ) UpdateUser ( ctx context . Context , req * UpdateUserRequest ) ( * types . User , error ) {
var cgt * datamanager . ChangeGroupsUpdateToken
cgNames := [ ] string { }
var user * types . User
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-05-03 07:53:38 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
2019-05-03 09:07:53 +00:00
user , err = s . readDB . GetUser ( tx , req . UserRef )
2019-05-03 07:53:38 +00:00
if err != nil {
return err
}
if user == nil {
2019-05-03 09:07:53 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "user %q doesn't exist" , req . UserRef ) )
2019-05-03 07:53:38 +00:00
}
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
if req . UserName != "" {
// check duplicate user name
u , err := s . readDB . GetUserByName ( tx , req . UserName )
if err != nil {
return err
}
if u != nil {
return util . NewErrBadRequest ( errors . Errorf ( "user with name %q already exists" , u . Name ) )
}
// changegroup is the username (and in future the email) to ensure no
// concurrent user creation/modification using the same name
cgNames = append ( cgNames , util . EncodeSha256Hex ( "username-" + req . UserName ) )
}
return nil
} )
if err != nil {
return nil , err
}
if req . UserName != "" {
user . Name = req . UserName
}
userj , err := json . Marshal ( user )
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal user" )
}
actions := [ ] * datamanager . Action {
{
ActionType : datamanager . ActionTypePut ,
DataType : string ( types . ConfigTypeUser ) ,
ID : user . ID ,
Data : userj ,
} ,
}
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
return user , err
}
2019-02-21 15:08:30 +00:00
type CreateUserLARequest struct {
2019-05-03 09:07:53 +00:00
UserRef string
2019-04-29 12:57:07 +00:00
RemoteSourceName string
RemoteUserID string
RemoteUserName string
UserAccessToken string
Oauth2AccessToken string
Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time . Time
2019-02-21 15:08:30 +00:00
}
func ( s * CommandHandler ) CreateUserLA ( ctx context . Context , req * CreateUserLARequest ) ( * types . LinkedAccount , error ) {
2019-05-03 09:07:53 +00:00
if req . UserRef == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "user ref required" ) )
2019-02-21 15:08:30 +00:00
}
if req . RemoteSourceName == "" {
2019-03-12 14:12:19 +00:00
return nil , util . NewErrBadRequest ( errors . Errorf ( "remote source name required" ) )
2019-02-21 15:08:30 +00:00
}
var user * types . User
var rs * types . RemoteSource
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-02-21 15:08:30 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-21 15:08:30 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
2019-05-03 09:07:53 +00:00
user , err = s . readDB . GetUser ( tx , req . UserRef )
2019-02-21 15:08:30 +00:00
if err != nil {
return err
}
if user == nil {
2019-05-03 09:07:53 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "user %q doesn't exist" , req . UserRef ) )
2019-02-21 15:08:30 +00:00
}
2019-04-29 08:37:23 +00:00
// changegroup is the userid
cgNames := [ ] string { util . EncodeSha256Hex ( "userid-" + user . ID ) }
2019-02-21 15:08:30 +00:00
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
rs , err = s . readDB . GetRemoteSourceByName ( tx , req . RemoteSourceName )
if err != nil {
return err
}
if rs == nil {
2019-03-12 14:12:19 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "remote source %q doesn't exist" , req . RemoteSourceName ) )
2019-02-21 15:08:30 +00:00
}
2019-03-29 16:50:51 +00:00
user , err := s . readDB . GetUserByLinkedAccountRemoteUserIDandSource ( tx , req . RemoteUserID , rs . ID )
if err != nil {
return errors . Wrapf ( err , "failed to get user for remote user id %q and remote source %q" , req . RemoteUserID , rs . ID )
}
if user != nil {
return util . NewErrBadRequest ( errors . Errorf ( "user for remote user id %q for remote source %q already exists" , req . RemoteUserID , req . RemoteSourceName ) )
}
2019-02-21 15:08:30 +00:00
return nil
} )
if err != nil {
return nil , err
}
if user . LinkedAccounts == nil {
user . LinkedAccounts = make ( map [ string ] * types . LinkedAccount )
}
la := & types . LinkedAccount {
2019-04-29 12:57:07 +00:00
ID : uuid . NewV4 ( ) . String ( ) ,
RemoteSourceID : rs . ID ,
RemoteUserID : req . RemoteUserID ,
RemoteUserName : req . RemoteUserName ,
UserAccessToken : req . UserAccessToken ,
Oauth2AccessToken : req . Oauth2AccessToken ,
Oauth2RefreshToken : req . Oauth2RefreshToken ,
Oauth2AccessTokenExpiresAt : req . Oauth2AccessTokenExpiresAt ,
2019-02-21 15:08:30 +00:00
}
user . LinkedAccounts [ la . ID ] = la
2019-02-28 14:52:35 +00:00
userj , err := json . Marshal ( user )
2019-02-21 15:08:30 +00:00
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal user" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-21 15:08:30 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeUser ) ,
ID : user . ID ,
2019-02-28 14:52:35 +00:00
Data : userj ,
2019-02-21 15:08:30 +00:00
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-21 15:08:30 +00:00
return la , err
}
2019-05-03 09:07:53 +00:00
func ( s * CommandHandler ) DeleteUserLA ( ctx context . Context , userRef , laID string ) error {
if userRef == "" {
return util . NewErrBadRequest ( errors . Errorf ( "user ref required" ) )
2019-02-21 15:08:30 +00:00
}
if laID == "" {
2019-03-12 14:12:19 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "user linked account id required" ) )
2019-02-21 15:08:30 +00:00
}
var user * types . User
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-02-21 15:08:30 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-21 15:08:30 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
2019-05-03 09:07:53 +00:00
user , err = s . readDB . GetUser ( tx , userRef )
2019-02-21 15:08:30 +00:00
if err != nil {
return err
}
if user == nil {
2019-05-03 09:07:53 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "user %q doesn't exist" , userRef ) )
2019-02-21 15:08:30 +00:00
}
2019-04-29 08:37:23 +00:00
// changegroup is the userid
cgNames := [ ] string { util . EncodeSha256Hex ( "userid-" + user . ID ) }
2019-02-21 15:08:30 +00:00
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
return nil
} )
if err != nil {
return err
}
_ , ok := user . LinkedAccounts [ laID ]
if ! ok {
2019-05-03 09:07:53 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "linked account id %q for user %q doesn't exist" , laID , userRef ) )
2019-02-21 15:08:30 +00:00
}
delete ( user . LinkedAccounts , laID )
2019-02-28 14:52:35 +00:00
userj , err := json . Marshal ( user )
2019-02-21 15:08:30 +00:00
if err != nil {
return errors . Wrapf ( err , "failed to marshal user" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-21 15:08:30 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeUser ) ,
ID : user . ID ,
2019-02-28 14:52:35 +00:00
Data : userj ,
2019-02-21 15:08:30 +00:00
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-21 15:08:30 +00:00
return err
}
type UpdateUserLARequest struct {
2019-05-03 09:07:53 +00:00
UserRef string
2019-04-29 12:57:07 +00:00
LinkedAccountID string
RemoteUserID string
RemoteUserName string
UserAccessToken string
Oauth2AccessToken string
Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time . Time
2019-02-21 15:08:30 +00:00
}
func ( s * CommandHandler ) UpdateUserLA ( ctx context . Context , req * UpdateUserLARequest ) ( * types . LinkedAccount , error ) {
2019-05-03 09:07:53 +00:00
if req . UserRef == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "user ref required" ) )
2019-02-21 15:08:30 +00:00
}
var user * types . User
var rs * types . RemoteSource
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-02-21 15:08:30 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-21 15:08:30 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
2019-05-03 09:07:53 +00:00
user , err = s . readDB . GetUser ( tx , req . UserRef )
2019-02-21 15:08:30 +00:00
if err != nil {
return err
}
if user == nil {
2019-05-03 09:07:53 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "user %q doesn't exist" , req . UserRef ) )
2019-02-21 15:08:30 +00:00
}
2019-04-29 08:37:23 +00:00
// changegroup is the userid
cgNames := [ ] string { util . EncodeSha256Hex ( "userid-" + user . ID ) }
2019-02-21 15:08:30 +00:00
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
la , ok := user . LinkedAccounts [ req . LinkedAccountID ]
if ! ok {
2019-04-30 10:56:43 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "linked account id %q for user %q doesn't exist" , req . LinkedAccountID , user . Name ) )
2019-02-21 15:08:30 +00:00
}
rs , err = s . readDB . GetRemoteSource ( tx , la . RemoteSourceID )
if err != nil {
return err
}
if rs == nil {
2019-03-12 14:12:19 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "remote source with id %q doesn't exist" , la . RemoteSourceID ) )
2019-02-21 15:08:30 +00:00
}
return nil
} )
if err != nil {
return nil , err
}
la := user . LinkedAccounts [ req . LinkedAccountID ]
la . RemoteUserID = req . RemoteUserID
la . RemoteUserName = req . RemoteUserName
la . UserAccessToken = req . UserAccessToken
la . Oauth2AccessToken = req . Oauth2AccessToken
la . Oauth2RefreshToken = req . Oauth2RefreshToken
2019-04-29 12:57:07 +00:00
la . Oauth2AccessTokenExpiresAt = req . Oauth2AccessTokenExpiresAt
2019-02-21 15:08:30 +00:00
userj , err := json . Marshal ( user )
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal user" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-21 15:08:30 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeUser ) ,
ID : user . ID ,
2019-02-21 15:08:30 +00:00
Data : userj ,
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-21 15:08:30 +00:00
return la , err
}
2019-05-03 09:07:53 +00:00
func ( s * CommandHandler ) CreateUserToken ( ctx context . Context , userRef , tokenName string ) ( string , error ) {
if userRef == "" {
return "" , util . NewErrBadRequest ( errors . Errorf ( "user ref required" ) )
2019-02-21 15:08:30 +00:00
}
2019-04-29 14:59:09 +00:00
if tokenName == "" {
return "" , util . NewErrBadRequest ( errors . Errorf ( "token name required" ) )
}
2019-02-21 15:08:30 +00:00
var user * types . User
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-02-21 15:08:30 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-21 15:08:30 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
2019-05-03 09:07:53 +00:00
user , err = s . readDB . GetUser ( tx , userRef )
2019-02-21 15:08:30 +00:00
if err != nil {
return err
}
if user == nil {
2019-05-03 09:07:53 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "user %q doesn't exist" , userRef ) )
2019-02-21 15:08:30 +00:00
}
2019-04-29 08:37:23 +00:00
// changegroup is the userid
cgNames := [ ] string { util . EncodeSha256Hex ( "userid-" + user . ID ) }
2019-02-21 15:08:30 +00:00
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
return nil
} )
if err != nil {
return "" , err
}
2019-04-05 13:06:26 +00:00
if user . Tokens != nil {
if _ , ok := user . Tokens [ tokenName ] ; ok {
2019-05-03 09:07:53 +00:00
return "" , util . NewErrBadRequest ( errors . Errorf ( "token %q for user %q already exists" , tokenName , userRef ) )
2019-04-05 13:06:26 +00:00
}
}
2019-02-21 15:08:30 +00:00
if user . Tokens == nil {
user . Tokens = make ( map [ string ] string )
}
token := util . EncodeSha1Hex ( uuid . NewV4 ( ) . String ( ) )
user . Tokens [ tokenName ] = token
userj , err := json . Marshal ( user )
if err != nil {
return "" , errors . Wrapf ( err , "failed to marshal user" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-21 15:08:30 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeUser ) ,
ID : user . ID ,
2019-02-21 15:08:30 +00:00
Data : userj ,
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-21 15:08:30 +00:00
return token , err
}
2019-05-03 09:07:53 +00:00
func ( s * CommandHandler ) DeleteUserToken ( ctx context . Context , userRef , tokenName string ) error {
if userRef == "" {
return util . NewErrBadRequest ( errors . Errorf ( "user ref required" ) )
2019-04-05 13:01:57 +00:00
}
if tokenName == "" {
return util . NewErrBadRequest ( errors . Errorf ( "token name required" ) )
}
var user * types . User
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-04-05 13:01:57 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-04-05 13:01:57 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
2019-05-03 09:07:53 +00:00
user , err = s . readDB . GetUser ( tx , userRef )
2019-04-05 13:01:57 +00:00
if err != nil {
return err
}
if user == nil {
2019-05-03 09:07:53 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "user %q doesn't exist" , userRef ) )
2019-04-05 13:01:57 +00:00
}
2019-04-29 08:37:23 +00:00
// changegroup is the userid
cgNames := [ ] string { util . EncodeSha256Hex ( "userid-" + user . ID ) }
2019-04-05 13:01:57 +00:00
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
return nil
} )
if err != nil {
return err
}
_ , ok := user . Tokens [ tokenName ]
if ! ok {
2019-05-03 09:07:53 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "token %q for user %q doesn't exist" , tokenName , userRef ) )
2019-04-05 13:01:57 +00:00
}
delete ( user . Tokens , tokenName )
userj , err := json . Marshal ( user )
if err != nil {
return errors . Wrapf ( err , "failed to marshal user" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-04-05 13:01:57 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-05 13:01:57 +00:00
DataType : string ( types . ConfigTypeUser ) ,
ID : user . ID ,
Data : userj ,
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-04-05 13:01:57 +00:00
return err
}
2019-02-21 15:08:30 +00:00
func ( s * CommandHandler ) CreateRemoteSource ( ctx context . Context , remoteSource * types . RemoteSource ) ( * types . RemoteSource , error ) {
if remoteSource . Name == "" {
2019-03-12 14:12:19 +00:00
return nil , util . NewErrBadRequest ( errors . Errorf ( "remotesource name required" ) )
2019-02-21 15:08:30 +00:00
}
2019-05-03 10:41:49 +00:00
if ! util . ValidateName ( remoteSource . Name ) {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid remotesource name %q" , remoteSource . Name ) )
}
if remoteSource . Name == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "remotesource name required" ) )
}
if remoteSource . APIURL == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "remotesource api url required" ) )
}
if remoteSource . Type == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "remotesource type required" ) )
}
if remoteSource . AuthType == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "remotesource auth type required" ) )
}
// validate if the remote source type supports the required auth type
if ! types . SourceSupportsAuthType ( types . RemoteSourceType ( remoteSource . Type ) , types . RemoteSourceAuthType ( remoteSource . AuthType ) ) {
return nil , util . NewErrBadRequest ( errors . Errorf ( "remotesource type %q doesn't support auth type %q" , remoteSource . Type , remoteSource . AuthType ) )
}
if remoteSource . AuthType == types . RemoteSourceAuthTypeOauth2 {
if remoteSource . Oauth2ClientID == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "remotesource oauth2clientid required for auth type %q" , types . RemoteSourceAuthTypeOauth2 ) )
}
if remoteSource . Oauth2ClientSecret == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "remotesource oauth2clientsecret required for auth type %q" , types . RemoteSourceAuthTypeOauth2 ) )
}
}
2019-02-21 15:08:30 +00:00
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-04-29 08:37:23 +00:00
// changegroup is the remotesource name
cgNames := [ ] string { util . EncodeSha256Hex ( "remotesourcename-" + remoteSource . Name ) }
2019-02-21 15:08:30 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-21 15:08:30 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
// check duplicate remoteSource name
u , err := s . readDB . GetRemoteSourceByName ( tx , remoteSource . Name )
if err != nil {
return err
}
if u != nil {
2019-03-12 14:12:19 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "remoteSource %q already exists" , u . Name ) )
2019-02-21 15:08:30 +00:00
}
return nil
} )
if err != nil {
return nil , err
}
remoteSource . ID = uuid . NewV4 ( ) . String ( )
rsj , err := json . Marshal ( remoteSource )
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal remotesource" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-21 15:08:30 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeRemoteSource ) ,
ID : remoteSource . ID ,
2019-02-21 15:08:30 +00:00
Data : rsj ,
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-21 15:08:30 +00:00
return remoteSource , err
}
func ( s * CommandHandler ) DeleteRemoteSource ( ctx context . Context , remoteSourceName string ) error {
var remoteSource * types . RemoteSource
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-04-29 08:37:23 +00:00
// changegroup is the remotesource id
cgNames := [ ] string { util . EncodeSha256Hex ( "remotesourceid-" + remoteSource . ID ) }
2019-02-21 15:08:30 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-21 15:08:30 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
// check remoteSource existance
remoteSource , err = s . readDB . GetRemoteSourceByName ( tx , remoteSourceName )
if err != nil {
return err
}
if remoteSource == nil {
2019-03-12 14:12:19 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "remotesource %q doesn't exist" , remoteSourceName ) )
2019-02-21 15:08:30 +00:00
}
return nil
} )
if err != nil {
return err
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-21 15:08:30 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypeDelete ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeRemoteSource ) ,
ID : remoteSource . ID ,
2019-02-21 15:08:30 +00:00
} ,
}
// changegroup is all the remote sources
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-21 15:08:30 +00:00
return err
}
2019-02-28 14:52:35 +00:00
func ( s * CommandHandler ) CreateOrg ( ctx context . Context , org * types . Organization ) ( * types . Organization , error ) {
if org . Name == "" {
2019-05-03 10:41:49 +00:00
return nil , util . NewErrBadRequest ( errors . Errorf ( "organization name required" ) )
}
if ! util . ValidateName ( org . Name ) {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid organization name %q" , org . Name ) )
2019-02-28 14:52:35 +00:00
}
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-04-29 08:37:23 +00:00
// changegroup is the org name
cgNames := [ ] string { util . EncodeSha256Hex ( "orgname-" + org . Name ) }
2019-02-28 14:52:35 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-28 14:52:35 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
// check duplicate org name
u , err := s . readDB . GetOrgByName ( tx , org . Name )
if err != nil {
return err
}
if u != nil {
2019-03-12 14:12:19 +00:00
return util . NewErrBadRequest ( errors . Errorf ( "org %q already exists" , u . Name ) )
2019-02-28 14:52:35 +00:00
}
return nil
} )
if err != nil {
return nil , err
}
org . ID = uuid . NewV4 ( ) . String ( )
orgj , err := json . Marshal ( org )
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal org" )
}
2019-03-14 13:36:18 +00:00
pg := & types . ProjectGroup {
ID : uuid . NewV4 ( ) . String ( ) ,
Parent : types . Parent {
Type : types . ConfigTypeOrg ,
ID : org . ID ,
} ,
}
pgj , err := json . Marshal ( pg )
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal project group" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-28 14:52:35 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeOrg ) ,
ID : org . ID ,
2019-02-28 14:52:35 +00:00
Data : orgj ,
} ,
2019-03-14 13:36:18 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeProjectGroup ) ,
ID : pg . ID ,
2019-03-14 13:36:18 +00:00
Data : pgj ,
} ,
2019-02-28 14:52:35 +00:00
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-28 14:52:35 +00:00
return org , err
}
2019-05-03 09:07:53 +00:00
func ( s * CommandHandler ) DeleteOrg ( ctx context . Context , orgRef string ) error {
2019-02-28 14:52:35 +00:00
var org * types . Organization
var projects [ ] * types . Project
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-02-28 14:52:35 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-02-28 14:52:35 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
2019-05-03 09:07:53 +00:00
// check org existance
org , err = s . readDB . GetOrgByName ( tx , orgRef )
2019-02-28 14:52:35 +00:00
if err != nil {
return err
}
2019-05-03 09:07:53 +00:00
if org == nil {
return util . NewErrBadRequest ( errors . Errorf ( "org %q doesn't exist" , orgRef ) )
}
2019-02-28 14:52:35 +00:00
2019-05-03 09:07:53 +00:00
// changegroup is the org id
cgNames := [ ] string { util . EncodeSha256Hex ( "orgid-" + org . ID ) }
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
2019-02-28 14:52:35 +00:00
if err != nil {
return err
}
2019-05-03 09:07:53 +00:00
2019-02-28 14:52:35 +00:00
return nil
} )
if err != nil {
return err
}
2019-05-03 09:07:53 +00:00
// TODO(sgotti) delete all project groups, projects etc...
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-02-28 14:52:35 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypeDelete ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeOrg ) ,
ID : org . ID ,
2019-02-28 14:52:35 +00:00
} ,
}
// delete all org projects
for _ , project := range projects {
2019-04-26 14:00:03 +00:00
actions = append ( actions , & datamanager . Action {
ActionType : datamanager . ActionTypeDelete ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeProject ) ,
ID : project . ID ,
2019-02-28 14:52:35 +00:00
} )
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-02-28 14:52:35 +00:00
return err
}
2019-03-14 13:36:18 +00:00
func ( s * CommandHandler ) CreateSecret ( ctx context . Context , secret * types . Secret ) ( * types . Secret , error ) {
if secret . Name == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "secret name required" ) )
}
2019-05-03 10:41:49 +00:00
if ! util . ValidateName ( secret . Name ) {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid secret name %q" , secret . Name ) )
}
2019-04-05 08:32:32 +00:00
if secret . Type != types . SecretTypeInternal {
2019-05-03 10:41:49 +00:00
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid secret type %q" , secret . Type ) )
2019-04-05 08:32:32 +00:00
}
switch secret . Type {
case types . SecretTypeInternal :
if len ( secret . Data ) == 0 {
return nil , util . NewErrBadRequest ( errors . Errorf ( "empty secret data" ) )
}
}
2019-03-14 13:36:18 +00:00
if secret . Parent . Type == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "secret parent type required" ) )
}
if secret . Parent . ID == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "secret parentid required" ) )
}
if secret . Parent . Type != types . ConfigTypeProject && secret . Parent . Type != types . ConfigTypeProjectGroup {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid secret parent type %q" , secret . Parent . Type ) )
}
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-04-29 08:37:23 +00:00
// changegroup is the secret name
cgNames := [ ] string { util . EncodeSha256Hex ( "secretname-" + secret . Name ) }
2019-03-14 13:36:18 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-03-14 13:36:18 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
parentID , err := s . readDB . ResolveConfigID ( tx , secret . Parent . Type , secret . Parent . ID )
if err != nil {
return err
}
secret . Parent . ID = parentID
// check duplicate secret name
s , err := s . readDB . GetSecretByName ( tx , secret . Parent . ID , secret . Name )
if err != nil {
return err
}
if s != nil {
return util . NewErrBadRequest ( errors . Errorf ( "secret with name %q for %s with id %q already exists" , secret . Name , secret . Parent . Type , secret . Parent . ID ) )
}
return nil
} )
if err != nil {
return nil , err
}
secret . ID = uuid . NewV4 ( ) . String ( )
secretj , err := json . Marshal ( secret )
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal secret" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-03-14 13:36:18 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeSecret ) ,
ID : secret . ID ,
2019-03-14 13:36:18 +00:00
Data : secretj ,
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-03-14 13:36:18 +00:00
return secret , err
}
func ( s * CommandHandler ) DeleteSecret ( ctx context . Context , parentType types . ConfigType , parentRef , secretName string ) error {
var secret * types . Secret
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-03-14 13:36:18 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-03-14 13:36:18 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
parentID , err := s . readDB . ResolveConfigID ( tx , parentType , parentRef )
if err != nil {
return err
}
// check secret existance
secret , err = s . readDB . GetSecretByName ( tx , parentID , secretName )
if err != nil {
return err
}
if secret == nil {
return util . NewErrBadRequest ( errors . Errorf ( "secret with name %q doesn't exist" , secretName ) )
}
2019-04-29 08:37:23 +00:00
// changegroup is the secret id
cgNames := [ ] string { util . EncodeSha256Hex ( "secretid-" + secret . ID ) }
2019-03-14 13:36:18 +00:00
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
return nil
} )
if err != nil {
return err
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-03-14 13:36:18 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypeDelete ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeSecret ) ,
ID : secret . ID ,
2019-03-14 13:36:18 +00:00
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-03-14 13:36:18 +00:00
return err
}
func ( s * CommandHandler ) CreateVariable ( ctx context . Context , variable * types . Variable ) ( * types . Variable , error ) {
if variable . Name == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "variable name required" ) )
}
2019-05-03 10:41:49 +00:00
if ! util . ValidateName ( variable . Name ) {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid variable name %q" , variable . Name ) )
}
2019-04-02 09:07:39 +00:00
if len ( variable . Values ) == 0 {
return nil , util . NewErrBadRequest ( errors . Errorf ( "variable values required" ) )
}
2019-03-14 13:36:18 +00:00
if variable . Parent . Type == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "variable parent type required" ) )
}
if variable . Parent . ID == "" {
return nil , util . NewErrBadRequest ( errors . Errorf ( "variable parent id required" ) )
}
if variable . Parent . Type != types . ConfigTypeProject && variable . Parent . Type != types . ConfigTypeProjectGroup {
return nil , util . NewErrBadRequest ( errors . Errorf ( "invalid variable parent type %q" , variable . Parent . Type ) )
}
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-04-29 08:37:23 +00:00
// changegroup is the variable name
cgNames := [ ] string { util . EncodeSha256Hex ( "variablename-" + variable . Name ) }
2019-03-14 13:36:18 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-03-14 13:36:18 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
parentID , err := s . readDB . ResolveConfigID ( tx , variable . Parent . Type , variable . Parent . ID )
if err != nil {
return err
}
variable . Parent . ID = parentID
// check duplicate variable name
s , err := s . readDB . GetVariableByName ( tx , variable . Parent . ID , variable . Name )
if err != nil {
return err
}
if s != nil {
return util . NewErrBadRequest ( errors . Errorf ( "variable with name %q for %s with id %q already exists" , variable . Name , variable . Parent . Type , variable . Parent . ID ) )
}
return nil
} )
if err != nil {
return nil , err
}
variable . ID = uuid . NewV4 ( ) . String ( )
variablej , err := json . Marshal ( variable )
if err != nil {
return nil , errors . Wrapf ( err , "failed to marshal variable" )
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-03-14 13:36:18 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypePut ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeVariable ) ,
ID : variable . ID ,
2019-03-14 13:36:18 +00:00
Data : variablej ,
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-03-14 13:36:18 +00:00
return variable , err
}
func ( s * CommandHandler ) DeleteVariable ( ctx context . Context , parentType types . ConfigType , parentRef , variableName string ) error {
var variable * types . Variable
2019-04-26 14:00:03 +00:00
var cgt * datamanager . ChangeGroupsUpdateToken
2019-03-14 13:36:18 +00:00
2019-05-03 10:41:49 +00:00
// must do all the checks in a single transaction to avoid concurrent changes
2019-03-14 13:36:18 +00:00
err := s . readDB . Do ( func ( tx * db . Tx ) error {
var err error
parentID , err := s . readDB . ResolveConfigID ( tx , parentType , parentRef )
if err != nil {
return err
}
// check variable existance
variable , err = s . readDB . GetVariableByName ( tx , parentID , variableName )
if err != nil {
return err
}
if variable == nil {
return util . NewErrBadRequest ( errors . Errorf ( "variable with name %q doesn't exist" , variableName ) )
}
2019-04-29 08:37:23 +00:00
// changegroup is the variable id
cgNames := [ ] string { util . EncodeSha256Hex ( "variableid-" + variable . ID ) }
2019-03-14 13:36:18 +00:00
cgt , err = s . readDB . GetChangeGroupsUpdateTokens ( tx , cgNames )
if err != nil {
return err
}
return nil
} )
if err != nil {
return err
}
2019-04-26 14:00:03 +00:00
actions := [ ] * datamanager . Action {
2019-03-14 13:36:18 +00:00
{
2019-04-26 14:00:03 +00:00
ActionType : datamanager . ActionTypeDelete ,
2019-04-01 10:54:43 +00:00
DataType : string ( types . ConfigTypeVariable ) ,
ID : variable . ID ,
2019-03-14 13:36:18 +00:00
} ,
}
2019-04-26 14:00:03 +00:00
_ , err = s . dm . WriteWal ( ctx , actions , cgt )
2019-03-14 13:36:18 +00:00
return err
}