2021-11-11 15:10:24 +00:00
package database
2022-01-19 09:55:38 +01:00
//nolint:goimports
2021-11-11 15:10:24 +00:00
import (
"context"
2021-12-16 14:28:16 +01:00
"fmt"
2022-03-01 08:21:55 +00:00
"strings"
2022-01-19 09:55:38 +01:00
"time"
2021-11-11 15:10:24 +00:00
2022-10-19 09:02:15 -04:00
"github.com/grafana/grafana/pkg/infra/db"
2022-06-15 15:59:40 +03:00
"github.com/grafana/grafana/pkg/infra/kvstore"
2021-12-16 14:28:16 +01:00
"github.com/grafana/grafana/pkg/infra/log"
2022-03-04 12:04:07 +01:00
"github.com/grafana/grafana/pkg/services/accesscontrol"
2022-08-03 14:13:05 +02:00
"github.com/grafana/grafana/pkg/services/apikey"
2022-08-10 11:56:48 +02:00
"github.com/grafana/grafana/pkg/services/org"
2021-11-11 15:10:24 +00:00
"github.com/grafana/grafana/pkg/services/serviceaccounts"
2022-12-16 17:09:06 +01:00
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
2022-06-28 14:32:25 +02:00
"github.com/grafana/grafana/pkg/services/user"
2022-12-13 14:56:10 +01:00
"github.com/grafana/grafana/pkg/setting"
2021-11-11 15:10:24 +00:00
)
type ServiceAccountsStoreImpl struct {
2022-12-13 14:56:10 +01:00
cfg * setting . Cfg
sqlStore db . DB
2022-08-03 14:13:05 +02:00
apiKeyService apikey . Service
kvStore kvstore . KVStore
log log . Logger
2022-09-23 11:59:07 +02:00
orgService org . Service
2022-12-13 14:56:10 +01:00
userService user . Service
2021-11-11 15:10:24 +00:00
}
2022-12-13 14:56:10 +01:00
func ProvideServiceAccountsStore ( cfg * setting . Cfg , store db . DB , apiKeyService apikey . Service ,
2022-12-07 17:03:22 +01:00
kvStore kvstore . KVStore , userService user . Service , orgService org . Service ) * ServiceAccountsStoreImpl {
2021-11-11 15:10:24 +00:00
return & ServiceAccountsStoreImpl {
2022-12-13 14:56:10 +01:00
cfg : cfg ,
2022-08-03 14:13:05 +02:00
sqlStore : store ,
apiKeyService : apiKeyService ,
kvStore : kvStore ,
log : log . New ( "serviceaccounts.store" ) ,
2022-09-23 11:59:07 +02:00
orgService : orgService ,
2022-12-07 17:03:22 +01:00
userService : userService ,
2021-11-11 15:10:24 +00:00
}
}
2022-06-15 15:59:40 +03:00
// CreateServiceAccount creates service account
2022-07-07 17:32:56 +01:00
func ( s * ServiceAccountsStoreImpl ) CreateServiceAccount ( ctx context . Context , orgId int64 , saForm * serviceaccounts . CreateServiceAccountForm ) ( * serviceaccounts . ServiceAccountDTO , error ) {
generatedLogin := "sa-" + strings . ToLower ( saForm . Name )
2022-03-08 11:07:58 +00:00
generatedLogin = strings . ReplaceAll ( generatedLogin , " " , "-" )
2022-07-07 17:32:56 +01:00
isDisabled := false
2022-08-10 11:56:48 +02:00
role := org . RoleViewer
2022-07-07 17:32:56 +01:00
if saForm . IsDisabled != nil {
isDisabled = * saForm . IsDisabled
}
if saForm . Role != nil {
role = * saForm . Role
}
2022-07-07 12:50:38 +00:00
2023-03-01 16:31:20 +00:00
newSA , err := s . userService . CreateServiceAccount ( ctx , & user . CreateUserCommand {
Login : generatedLogin ,
OrgID : orgId ,
Name : saForm . Name ,
IsDisabled : isDisabled ,
IsServiceAccount : true ,
DefaultOrgRole : string ( role ) ,
2022-07-07 12:50:38 +00:00
} )
2023-03-01 16:31:20 +00:00
if err != nil {
return nil , fmt . Errorf ( "failed to create service account: %w" , err )
2021-12-14 14:39:25 +01:00
}
2022-03-08 11:07:58 +00:00
2022-02-08 14:31:34 +01:00
return & serviceaccounts . ServiceAccountDTO {
2022-07-07 17:32:56 +01:00
Id : newSA . ID ,
Name : newSA . Name ,
Login : newSA . Login ,
OrgId : newSA . OrgID ,
Tokens : 0 ,
Role : string ( role ) ,
IsDisabled : isDisabled ,
2022-02-08 14:31:34 +01:00
} , nil
2021-12-14 14:39:25 +01:00
}
2021-11-11 15:10:24 +00:00
2022-06-15 15:59:40 +03:00
// UpdateServiceAccount updates service account
2022-12-13 14:56:10 +01:00
func ( s * ServiceAccountsStoreImpl ) UpdateServiceAccount (
ctx context . Context ,
2022-06-15 15:59:40 +03:00
orgId , serviceAccountId int64 ,
2022-12-13 14:56:10 +01:00
saForm * serviceaccounts . UpdateServiceAccountForm ,
) ( * serviceaccounts . ServiceAccountProfileDTO , error ) {
2022-06-15 15:59:40 +03:00
updatedUser := & serviceaccounts . ServiceAccountProfileDTO { }
2022-10-19 09:02:15 -04:00
err := s . sqlStore . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-06-15 15:59:40 +03:00
var err error
updatedUser , err = s . RetrieveServiceAccount ( ctx , orgId , serviceAccountId )
2021-11-11 15:10:24 +00:00
if err != nil {
return err
}
2022-06-15 15:59:40 +03:00
if saForm . Name == nil && saForm . Role == nil && saForm . IsDisabled == nil {
return nil
2022-03-14 17:24:07 +00:00
}
2022-06-15 15:59:40 +03:00
updateTime := time . Now ( )
if saForm . Role != nil {
2023-01-09 14:39:53 +01:00
var orgUser org . OrgUser
2022-06-15 15:59:40 +03:00
orgUser . Role = * saForm . Role
orgUser . Updated = updateTime
if _ , err := sess . Where ( "org_id = ? AND user_id = ?" , orgId , serviceAccountId ) . Update ( & orgUser ) ; err != nil {
2022-03-14 17:24:07 +00:00
return err
}
2022-06-15 15:59:40 +03:00
updatedUser . Role = string ( * saForm . Role )
2022-03-14 17:24:07 +00:00
}
2022-06-15 15:59:40 +03:00
if saForm . Name != nil || saForm . IsDisabled != nil {
2022-06-28 14:32:25 +02:00
user := user . User {
2022-06-15 15:59:40 +03:00
Updated : updateTime ,
}
if saForm . IsDisabled != nil {
user . IsDisabled = * saForm . IsDisabled
updatedUser . IsDisabled = * saForm . IsDisabled
sess . UseBool ( "is_disabled" )
}
if saForm . Name != nil {
user . Name = * saForm . Name
updatedUser . Name = * saForm . Name
}
if _ , err := sess . ID ( serviceAccountId ) . Update ( & user ) ; err != nil {
return err
}
}
2022-03-14 17:24:07 +00:00
return nil
} )
2022-06-15 15:59:40 +03:00
return updatedUser , err
2021-11-11 15:10:24 +00:00
}
2021-12-16 14:28:16 +01:00
2022-12-16 17:09:06 +01:00
func ServiceAccountDeletions ( dialect migrator . Dialect ) [ ] string {
2022-06-15 15:59:40 +03:00
deletes := [ ] string {
"DELETE FROM api_key WHERE service_account_id = ?" ,
2021-12-16 14:28:16 +01:00
}
2022-12-16 17:09:06 +01:00
deletes = append ( deletes , serviceAccountDeletions ( dialect ) ... )
2022-06-15 15:59:40 +03:00
return deletes
2021-12-16 14:28:16 +01:00
}
2022-01-12 13:23:00 +01:00
2022-06-15 15:59:40 +03:00
// DeleteServiceAccount deletes service account and all associated tokens
func ( s * ServiceAccountsStoreImpl ) DeleteServiceAccount ( ctx context . Context , orgId , serviceAccountId int64 ) error {
2022-10-19 09:02:15 -04:00
return s . sqlStore . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-06-15 15:59:40 +03:00
return s . deleteServiceAccount ( sess , orgId , serviceAccountId )
} )
2022-01-20 16:51:18 +01:00
}
2022-10-19 09:02:15 -04:00
func ( s * ServiceAccountsStoreImpl ) deleteServiceAccount ( sess * db . Session , orgId , serviceAccountId int64 ) error {
2022-06-28 14:32:25 +02:00
user := user . User { }
2022-06-15 15:59:40 +03:00
has , err := sess . Where ( ` org_id = ? and id = ? and is_service_account = ? ` ,
2022-12-13 14:56:10 +01:00
orgId , serviceAccountId , s . sqlStore . GetDialect ( ) . BooleanStr ( true ) ) . Get ( & user )
2022-06-15 15:59:40 +03:00
if err != nil {
return err
2022-01-20 16:51:18 +01:00
}
2022-06-15 15:59:40 +03:00
if ! has {
2023-03-08 11:32:09 +00:00
return serviceaccounts . ErrServiceAccountNotFound . Errorf ( "service account with id %d not found" , serviceAccountId )
2022-01-20 16:51:18 +01:00
}
2022-12-16 17:09:06 +01:00
for _ , sql := range ServiceAccountDeletions ( s . sqlStore . GetDialect ( ) ) {
2022-06-28 14:32:25 +02:00
_ , err := sess . Exec ( sql , user . ID )
2022-06-15 15:59:40 +03:00
if err != nil {
return err
}
2022-03-14 17:24:07 +00:00
}
2022-01-20 16:51:18 +01:00
return nil
}
2022-06-15 15:59:40 +03:00
// RetrieveServiceAccount returns a service account by its ID
func ( s * ServiceAccountsStoreImpl ) RetrieveServiceAccount ( ctx context . Context , orgId , serviceAccountId int64 ) ( * serviceaccounts . ServiceAccountProfileDTO , error ) {
2022-03-01 08:21:55 +00:00
serviceAccount := & serviceaccounts . ServiceAccountProfileDTO { }
2022-10-19 09:02:15 -04:00
err := s . sqlStore . WithDbSession ( ctx , func ( dbSession * db . Session ) error {
2022-03-01 08:21:55 +00:00
sess := dbSession . Table ( "org_user" )
2022-12-13 14:56:10 +01:00
sess . Join ( "INNER" , s . sqlStore . GetDialect ( ) . Quote ( "user" ) ,
fmt . Sprintf ( "org_user.user_id=%s.id" , s . sqlStore . GetDialect ( ) . Quote ( "user" ) ) )
2022-03-01 08:21:55 +00:00
whereConditions := make ( [ ] string , 0 , 3 )
whereParams := make ( [ ] interface { } , 0 )
whereConditions = append ( whereConditions , "org_user.org_id = ?" )
2022-06-15 15:59:40 +03:00
whereParams = append ( whereParams , orgId )
2022-03-01 08:21:55 +00:00
whereConditions = append ( whereConditions , "org_user.user_id = ?" )
2022-06-15 15:59:40 +03:00
whereParams = append ( whereParams , serviceAccountId )
2022-03-01 08:21:55 +00:00
whereConditions = append ( whereConditions ,
fmt . Sprintf ( "%s.is_service_account = %s" ,
2022-12-13 14:56:10 +01:00
s . sqlStore . GetDialect ( ) . Quote ( "user" ) ,
s . sqlStore . GetDialect ( ) . BooleanStr ( true ) ) )
2022-03-01 08:21:55 +00:00
sess . Where ( strings . Join ( whereConditions , " AND " ) , whereParams ... )
sess . Cols (
"org_user.user_id" ,
"org_user.org_id" ,
"org_user.role" ,
"user.email" ,
"user.name" ,
"user.login" ,
"user.created" ,
"user.updated" ,
"user.is_disabled" ,
)
if ok , err := sess . Get ( serviceAccount ) ; err != nil {
return err
} else if ! ok {
2023-03-08 11:32:09 +00:00
return serviceaccounts . ErrServiceAccountNotFound . Errorf ( "service account with id %d not found" , serviceAccountId )
2022-03-01 08:21:55 +00:00
}
return nil
} )
2022-06-02 13:14:48 +01:00
return serviceAccount , err
2022-01-19 09:23:46 +00:00
}
2022-01-20 16:51:18 +01:00
2022-06-15 15:59:40 +03:00
func ( s * ServiceAccountsStoreImpl ) RetrieveServiceAccountIdByName ( ctx context . Context , orgId int64 , name string ) ( int64 , error ) {
2022-04-12 19:34:04 +02:00
serviceAccount := & struct {
Id int64
} { }
2022-10-19 09:02:15 -04:00
err := s . sqlStore . WithDbSession ( ctx , func ( dbSession * db . Session ) error {
2022-04-12 19:34:04 +02:00
sess := dbSession . Table ( "user" )
whereConditions := [ ] string {
fmt . Sprintf ( "%s.name = ?" ,
2022-12-13 14:56:10 +01:00
s . sqlStore . GetDialect ( ) . Quote ( "user" ) ) ,
2022-04-12 19:34:04 +02:00
fmt . Sprintf ( "%s.org_id = ?" ,
2022-12-13 14:56:10 +01:00
s . sqlStore . GetDialect ( ) . Quote ( "user" ) ) ,
2022-04-12 19:34:04 +02:00
fmt . Sprintf ( "%s.is_service_account = %s" ,
2022-12-13 14:56:10 +01:00
s . sqlStore . GetDialect ( ) . Quote ( "user" ) ,
s . sqlStore . GetDialect ( ) . BooleanStr ( true ) ) ,
2022-04-12 19:34:04 +02:00
}
2022-06-15 15:59:40 +03:00
whereParams := [ ] interface { } { name , orgId }
2022-04-12 19:34:04 +02:00
sess . Where ( strings . Join ( whereConditions , " AND " ) , whereParams ... )
sess . Cols (
"user.id" ,
)
if ok , err := sess . Get ( serviceAccount ) ; err != nil {
return err
} else if ! ok {
2023-03-08 11:32:09 +00:00
return serviceaccounts . ErrServiceAccountNotFound . Errorf ( "service account with name %s not found" , name )
2022-04-12 19:34:04 +02:00
}
return nil
} )
if err != nil {
return 0 , err
}
return serviceAccount . Id , nil
}
2022-12-13 14:56:10 +01:00
func ( s * ServiceAccountsStoreImpl ) SearchOrgServiceAccounts ( ctx context . Context , query * serviceaccounts . SearchOrgServiceAccountsQuery ) ( * serviceaccounts . SearchOrgServiceAccountsResult , error ) {
searchResult := & serviceaccounts . SearchOrgServiceAccountsResult {
2022-03-14 17:24:07 +00:00
TotalCount : 0 ,
ServiceAccounts : make ( [ ] * serviceaccounts . ServiceAccountDTO , 0 ) ,
2022-12-13 14:56:10 +01:00
Page : query . Page ,
PerPage : query . Limit ,
2022-03-14 17:24:07 +00:00
}
2022-03-04 12:04:07 +01:00
2022-10-19 09:02:15 -04:00
err := s . sqlStore . WithDbSession ( ctx , func ( dbSession * db . Session ) error {
2022-03-04 12:04:07 +01:00
sess := dbSession . Table ( "org_user" )
2022-12-13 14:56:10 +01:00
sess . Join ( "INNER" , s . sqlStore . GetDialect ( ) . Quote ( "user" ) , fmt . Sprintf ( "org_user.user_id=%s.id" , s . sqlStore . GetDialect ( ) . Quote ( "user" ) ) )
2022-03-04 12:04:07 +01:00
whereConditions := make ( [ ] string , 0 )
whereParams := make ( [ ] interface { } , 0 )
whereConditions = append ( whereConditions , "org_user.org_id = ?" )
2022-12-13 14:56:10 +01:00
whereParams = append ( whereParams , query . OrgID )
2022-03-04 12:04:07 +01:00
2022-03-08 13:10:16 +00:00
whereConditions = append ( whereConditions ,
fmt . Sprintf ( "%s.is_service_account = %s" ,
2022-12-13 14:56:10 +01:00
s . sqlStore . GetDialect ( ) . Quote ( "user" ) ,
s . sqlStore . GetDialect ( ) . BooleanStr ( true ) ) )
2022-03-04 12:04:07 +01:00
2022-12-13 14:56:10 +01:00
if ! accesscontrol . IsDisabled ( s . cfg ) {
acFilter , err := accesscontrol . Filter ( query . SignedInUser , "org_user.user_id" , "serviceaccounts:id:" , serviceaccounts . ActionRead )
2022-03-04 12:04:07 +01:00
if err != nil {
return err
}
whereConditions = append ( whereConditions , acFilter . Where )
whereParams = append ( whereParams , acFilter . Args ... )
}
2022-12-13 14:56:10 +01:00
if query . Query != "" {
queryWithWildcards := "%" + query . Query + "%"
whereConditions = append ( whereConditions , "(email " + s . sqlStore . GetDialect ( ) . LikeStr ( ) + " ? OR name " + s . sqlStore . GetDialect ( ) . LikeStr ( ) + " ? OR login " + s . sqlStore . GetDialect ( ) . LikeStr ( ) + " ?)" )
2022-03-04 12:04:07 +01:00
whereParams = append ( whereParams , queryWithWildcards , queryWithWildcards , queryWithWildcards )
}
2022-12-13 14:56:10 +01:00
switch query . Filter {
2022-03-18 15:50:34 +01:00
case serviceaccounts . FilterIncludeAll :
// pass
case serviceaccounts . FilterOnlyExpiredTokens :
now := time . Now ( ) . Unix ( )
// we do a subquery to remove duplicates coming from joining in api_keys, if we find more than one api key that has expired
whereConditions = append (
whereConditions ,
"(SELECT count(*) FROM api_key WHERE api_key.service_account_id = org_user.user_id AND api_key.expires < ?) > 0" )
whereParams = append ( whereParams , now )
2022-06-01 10:35:16 +03:00
case serviceaccounts . FilterOnlyDisabled :
whereConditions = append (
whereConditions ,
"is_disabled = ?" )
2022-12-13 14:56:10 +01:00
whereParams = append ( whereParams , s . sqlStore . GetDialect ( ) . BooleanStr ( true ) )
2022-03-18 15:50:34 +01:00
default :
2022-12-13 14:56:10 +01:00
s . log . Warn ( "invalid filter user for service account filtering" , "service account search filtering" , query . Filter )
2022-03-18 15:50:34 +01:00
}
2022-03-04 12:04:07 +01:00
if len ( whereConditions ) > 0 {
sess . Where ( strings . Join ( whereConditions , " AND " ) , whereParams ... )
}
2022-12-13 14:56:10 +01:00
if query . Limit > 0 {
offset := query . Limit * ( query . Page - 1 )
sess . Limit ( query . Limit , offset )
2022-03-04 12:04:07 +01:00
}
sess . Cols (
"org_user.user_id" ,
"org_user.org_id" ,
"org_user.role" ,
"user.email" ,
"user.name" ,
"user.login" ,
"user.last_seen_at" ,
2022-03-08 13:10:16 +00:00
"user.is_disabled" ,
2022-03-04 12:04:07 +01:00
)
sess . Asc ( "user.email" , "user.login" )
2022-03-14 17:24:07 +00:00
if err := sess . Find ( & searchResult . ServiceAccounts ) ; err != nil {
2022-03-04 12:04:07 +01:00
return err
}
// get total
serviceaccount := serviceaccounts . ServiceAccountDTO { }
countSess := dbSession . Table ( "org_user" )
2022-12-13 14:56:10 +01:00
sess . Join ( "INNER" , s . sqlStore . GetDialect ( ) . Quote ( "user" ) , fmt . Sprintf ( "org_user.user_id=%s.id" , s . sqlStore . GetDialect ( ) . Quote ( "user" ) ) )
2022-03-04 12:04:07 +01:00
if len ( whereConditions ) > 0 {
countSess . Where ( strings . Join ( whereConditions , " AND " ) , whereParams ... )
}
count , err := countSess . Count ( & serviceaccount )
if err != nil {
return err
}
2022-03-14 17:24:07 +00:00
searchResult . TotalCount = count
2022-03-04 12:04:07 +01:00
return nil
} )
if err != nil {
return nil , err
}
2022-03-14 17:24:07 +00:00
return searchResult , nil
2022-03-04 12:04:07 +01:00
}
2022-06-15 15:59:40 +03:00
func ( s * ServiceAccountsStoreImpl ) MigrateApiKeysToServiceAccounts ( ctx context . Context , orgId int64 ) error {
2022-08-23 10:01:35 -05:00
basicKeys , err := s . apiKeyService . GetAllAPIKeys ( ctx , orgId )
if err != nil {
return err
}
2022-06-15 15:59:40 +03:00
if len ( basicKeys ) > 0 {
for _ , key := range basicKeys {
err := s . CreateServiceAccountFromApikey ( ctx , key )
if err != nil {
s . log . Error ( "migating to service accounts failed with error" , err )
return err
}
2023-02-03 17:23:09 +01:00
s . log . Debug ( "API key converted to service account token" , "keyId" , key . ID )
2022-01-20 16:51:18 +01:00
}
}
2022-06-15 15:59:40 +03:00
if err := s . kvStore . Set ( ctx , orgId , "serviceaccounts" , "migrationStatus" , "1" ) ; err != nil {
s . log . Error ( "Failed to write API keys migration status" , err )
}
return nil
}
func ( s * ServiceAccountsStoreImpl ) MigrateApiKey ( ctx context . Context , orgId int64 , keyId int64 ) error {
2022-08-23 10:01:35 -05:00
basicKeys , err := s . apiKeyService . GetAllAPIKeys ( ctx , orgId )
if err != nil {
return err
}
2022-06-15 15:59:40 +03:00
if len ( basicKeys ) == 0 {
return fmt . Errorf ( "no API keys to convert found" )
}
for _ , key := range basicKeys {
2023-02-03 17:23:09 +01:00
if keyId == key . ID {
2022-06-15 15:59:40 +03:00
err := s . CreateServiceAccountFromApikey ( ctx , key )
if err != nil {
s . log . Error ( "converting to service account failed with error" , "keyId" , keyId , "error" , err )
return err
}
}
}
return nil
}
2022-08-04 14:19:09 +02:00
func ( s * ServiceAccountsStoreImpl ) CreateServiceAccountFromApikey ( ctx context . Context , key * apikey . APIKey ) error {
2022-06-15 15:59:40 +03:00
prefix := "sa-autogen"
2022-06-28 14:32:25 +02:00
cmd := user . CreateUserCommand {
2023-02-03 17:23:09 +01:00
Login : fmt . Sprintf ( "%v-%v-%v" , prefix , key . OrgID , key . Name ) ,
2022-06-15 15:59:40 +03:00
Name : fmt . Sprintf ( "%v-%v" , prefix , key . Name ) ,
2023-02-03 17:23:09 +01:00
OrgID : key . OrgID ,
2022-06-15 15:59:40 +03:00
DefaultOrgRole : string ( key . Role ) ,
IsServiceAccount : true ,
}
2022-10-19 09:02:15 -04:00
return s . sqlStore . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-12-07 17:03:22 +01:00
newSA , errCreateSA := s . userService . CreateServiceAccount ( ctx , & cmd )
2022-06-15 15:59:40 +03:00
if errCreateSA != nil {
return fmt . Errorf ( "failed to create service account: %w" , errCreateSA )
}
2023-02-03 17:23:09 +01:00
if err := s . assignApiKeyToServiceAccount ( sess , key . ID , newSA . ID ) ; err != nil {
2022-06-15 15:59:40 +03:00
return fmt . Errorf ( "failed to migrate API key to service account token: %w" , err )
}
return nil
} )
}
2022-12-16 17:09:06 +01:00
func serviceAccountDeletions ( dialect migrator . Dialect ) [ ] string {
deletes := [ ] string {
"DELETE FROM star WHERE user_id = ?" ,
"DELETE FROM " + dialect . Quote ( "user" ) + " WHERE id = ?" ,
"DELETE FROM org_user WHERE user_id = ?" ,
"DELETE FROM dashboard_acl WHERE user_id = ?" ,
"DELETE FROM preferences WHERE user_id = ?" ,
"DELETE FROM team_member WHERE user_id = ?" ,
"DELETE FROM user_auth WHERE user_id = ?" ,
"DELETE FROM user_auth_token WHERE user_id = ?" ,
"DELETE FROM quota WHERE user_id = ?" ,
}
return deletes
}