2019-02-21 16:58:25 +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.
2019-05-03 21:48:49 +00:00
package action
2019-02-21 16:58:25 +00:00
import (
"context"
"encoding/json"
2019-06-11 13:09:41 +00:00
"fmt"
2019-08-02 08:03:28 +00:00
"regexp"
2019-06-11 13:09:41 +00:00
"strings"
2019-04-29 12:57:07 +00:00
"time"
2019-02-21 16:58:25 +00:00
2022-02-22 14:01:29 +00:00
"agola.io/agola/internal/errors"
2019-07-01 09:40:20 +00:00
gitsource "agola.io/agola/internal/gitsources"
"agola.io/agola/internal/gitsources/agolagit"
2022-02-24 10:18:29 +00:00
scommon "agola.io/agola/internal/services/common"
"agola.io/agola/internal/services/gateway/common"
2019-07-01 09:40:20 +00:00
"agola.io/agola/internal/services/types"
"agola.io/agola/internal/util"
2019-07-31 13:39:07 +00:00
csapitypes "agola.io/agola/services/configstore/api/types"
cstypes "agola.io/agola/services/configstore/types"
2019-02-21 16:58:25 +00:00
2021-09-10 10:33:59 +00:00
"github.com/golang-jwt/jwt/v4"
2019-02-21 16:58:25 +00:00
)
2019-04-29 13:42:10 +00:00
const (
expireTimeRange time . Duration = 5 * time . Minute
)
func isAccessTokenExpired ( expiresAt time . Time ) bool {
if expiresAt . IsZero ( ) {
return false
}
return expiresAt . Add ( - expireTimeRange ) . Before ( time . Now ( ) )
}
2019-07-31 13:17:54 +00:00
func ( h * ActionHandler ) GetUser ( ctx context . Context , userRef string ) ( * cstypes . User , error ) {
2022-02-24 10:18:29 +00:00
if ! common . IsUserLoggedOrAdmin ( ctx ) {
2019-05-03 21:19:23 +00:00
return nil , errors . Errorf ( "user not logged in" )
}
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUser ( ctx , userRef )
2019-05-05 12:45:19 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , err )
2019-05-05 12:45:19 +00:00
}
return user , nil
}
2022-01-26 15:52:10 +00:00
func ( h * ActionHandler ) GetUserOrgs ( ctx context . Context , userRef string ) ( [ ] * csapitypes . UserOrgsResponse , error ) {
2022-02-24 10:18:29 +00:00
if ! common . IsUserLogged ( ctx ) {
2022-01-26 15:52:10 +00:00
return nil , errors . Errorf ( "user not logged in" )
}
2022-02-21 11:19:55 +00:00
orgs , _ , err := h . configstoreClient . GetUserOrgs ( ctx , userRef )
2022-01-26 15:52:10 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , err )
2022-01-26 15:52:10 +00:00
}
return orgs , nil
}
2019-05-05 12:45:19 +00:00
type GetUsersRequest struct {
Start string
Limit int
Asc bool
}
2019-07-31 13:17:54 +00:00
func ( h * ActionHandler ) GetUsers ( ctx context . Context , req * GetUsersRequest ) ( [ ] * cstypes . User , error ) {
2022-02-24 10:18:29 +00:00
if ! common . IsUserAdmin ( ctx ) {
2019-05-03 21:19:23 +00:00
return nil , errors . Errorf ( "user not logged in" )
}
2022-02-21 11:19:55 +00:00
users , _ , err := h . configstoreClient . GetUsers ( ctx , req . Start , req . Limit , req . Asc )
2019-05-05 12:45:19 +00:00
if err != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , err )
2019-05-05 12:45:19 +00:00
}
return users , nil
}
2019-03-14 13:36:18 +00:00
type CreateUserRequest struct {
UserName string
}
2019-07-31 13:17:54 +00:00
func ( h * ActionHandler ) CreateUser ( ctx context . Context , req * CreateUserRequest ) ( * cstypes . User , error ) {
2022-02-24 10:18:29 +00:00
if ! common . IsUserAdmin ( ctx ) {
2019-05-03 21:19:23 +00:00
return nil , errors . Errorf ( "user not admin" )
}
2019-03-14 13:36:18 +00:00
if req . UserName == "" {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "user name required" ) )
2019-03-14 13:36:18 +00:00
}
if ! util . ValidateName ( req . UserName ) {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "invalid user name %q" , req . UserName ) )
2019-03-14 13:36:18 +00:00
}
2019-07-31 13:39:07 +00:00
creq := & csapitypes . CreateUserRequest {
2019-03-14 13:36:18 +00:00
UserName : req . UserName ,
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "creating user" )
2022-02-21 11:19:55 +00:00
u , _ , err := h . configstoreClient . CreateUser ( ctx , creq )
2019-03-14 13:36:18 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to create user" ) )
2019-03-14 13:36:18 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "user %s created, ID: %s" , u . Name , u . ID )
2019-03-14 13:36:18 +00:00
return u , nil
}
2019-04-05 13:56:06 +00:00
type CreateUserTokenRequest struct {
2019-05-03 09:07:53 +00:00
UserRef string
2019-04-05 13:56:06 +00:00
TokenName string
}
2019-05-03 21:48:49 +00:00
func ( h * ActionHandler ) CreateUserToken ( ctx context . Context , req * CreateUserTokenRequest ) ( string , error ) {
2022-02-24 10:18:29 +00:00
isAdmin := common . IsUserAdmin ( ctx )
userID := common . CurrentUserID ( ctx )
2019-04-05 13:56:06 +00:00
2019-05-03 09:07:53 +00:00
userRef := req . UserRef
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUser ( ctx , userRef )
2019-04-05 13:56:06 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return "" , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get user" ) )
2019-04-05 13:56:06 +00:00
}
// only admin or the same logged user can create a token
if ! isAdmin && user . ID != userID {
2022-02-21 11:19:55 +00:00
return "" , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "logged in user cannot create token for another user" ) )
2019-04-05 13:56:06 +00:00
}
if _ , ok := user . Tokens [ req . TokenName ] ; ok {
2022-02-21 11:19:55 +00:00
return "" , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "user %q already have a token with name %q" , userRef , req . TokenName ) )
2019-04-05 13:56:06 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "creating user token" )
2019-07-31 13:39:07 +00:00
creq := & csapitypes . CreateUserTokenRequest {
2019-04-05 13:56:06 +00:00
TokenName : req . TokenName ,
}
2022-02-21 11:19:55 +00:00
res , _ , err := h . configstoreClient . CreateUserToken ( ctx , userRef , creq )
2019-04-05 13:56:06 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return "" , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to create user token" ) )
2019-04-05 13:56:06 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "token %q for user %q created" , req . TokenName , userRef )
2019-04-05 13:56:06 +00:00
return res . Token , nil
}
2019-02-21 16:58:25 +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
UserAccessToken string
Oauth2AccessToken string
Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time . Time
2019-02-21 16:58:25 +00:00
}
2019-07-31 13:17:54 +00:00
func ( h * ActionHandler ) CreateUserLA ( ctx context . Context , req * CreateUserLARequest ) ( * cstypes . LinkedAccount , error ) {
2019-05-03 09:07:53 +00:00
userRef := req . UserRef
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUser ( ctx , userRef )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get user %q" , userRef ) )
2019-02-21 16:58:25 +00:00
}
2022-02-21 11:19:55 +00:00
rs , _ , err := h . configstoreClient . GetRemoteSource ( ctx , req . RemoteSourceName )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get remote source %q" , req . RemoteSourceName ) )
2019-02-21 16:58:25 +00:00
}
2019-07-31 13:17:54 +00:00
var la * cstypes . LinkedAccount
2019-02-21 16:58:25 +00:00
for _ , v := range user . LinkedAccounts {
if v . RemoteSourceID == rs . ID {
la = v
break
}
}
if la != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "user %q already have a linked account for remote source %q" , userRef , rs . Name ) )
2019-02-21 16:58:25 +00:00
}
2022-02-24 10:18:29 +00:00
accessToken , err := scommon . GetAccessToken ( rs , req . UserAccessToken , req . Oauth2AccessToken )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
2022-02-24 10:18:29 +00:00
userSource , err := scommon . GetUserSource ( rs , accessToken )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
remoteUserInfo , err := userSource . GetUserInfo ( )
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . Wrapf ( err , "failed to retrieve remote user info for remote source %q" , rs . ID )
2019-02-21 16:58:25 +00:00
}
if remoteUserInfo . ID == "" {
return nil , errors . Errorf ( "empty remote user id for remote source %q" , rs . ID )
}
2019-07-31 13:39:07 +00:00
creq := & csapitypes . CreateUserLARequest {
2019-04-29 12:57:07 +00:00
RemoteSourceName : req . RemoteSourceName ,
RemoteUserID : remoteUserInfo . ID ,
RemoteUserName : remoteUserInfo . LoginName ,
UserAccessToken : req . UserAccessToken ,
Oauth2AccessToken : req . Oauth2AccessToken ,
Oauth2RefreshToken : req . Oauth2RefreshToken ,
Oauth2AccessTokenExpiresAt : req . Oauth2AccessTokenExpiresAt ,
2019-02-21 16:58:25 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "creating linked account" )
2022-02-21 11:19:55 +00:00
la , _ , err = h . configstoreClient . CreateUserLA ( ctx , userRef , creq )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to create linked account" ) )
2019-02-21 16:58:25 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "linked account %q for user %q created" , la . ID , userRef )
2019-02-21 16:58:25 +00:00
return la , nil
}
2019-07-31 13:17:54 +00:00
func ( h * ActionHandler ) UpdateUserLA ( ctx context . Context , userRef string , la * cstypes . LinkedAccount ) error {
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUser ( ctx , userRef )
2019-04-29 13:42:10 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get user %q" , userRef ) )
2019-04-29 13:42:10 +00:00
}
laFound := false
for _ , ula := range user . LinkedAccounts {
if ula . ID == la . ID {
laFound = true
break
}
}
if ! laFound {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "user %q doesn't have a linked account with id %q" , userRef , la . ID ) )
2019-04-29 13:42:10 +00:00
}
2019-07-31 13:39:07 +00:00
creq := & csapitypes . UpdateUserLARequest {
2019-04-29 13:42:10 +00:00
RemoteUserID : la . RemoteUserID ,
RemoteUserName : la . RemoteUserName ,
UserAccessToken : la . UserAccessToken ,
Oauth2AccessToken : la . Oauth2AccessToken ,
Oauth2RefreshToken : la . Oauth2RefreshToken ,
Oauth2AccessTokenExpiresAt : la . Oauth2AccessTokenExpiresAt ,
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "updating user %q linked account" , userRef )
2022-02-21 11:19:55 +00:00
la , _ , err = h . configstoreClient . UpdateUserLA ( ctx , userRef , la . ID , creq )
2019-04-29 13:42:10 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to update user" ) )
2019-04-29 13:42:10 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "linked account %q for user %q updated" , la . ID , userRef )
2019-04-29 13:42:10 +00:00
return nil
}
// RefreshLinkedAccount refreshed the linked account oauth2 access token and update linked account in the configstore
2019-07-31 13:17:54 +00:00
func ( h * ActionHandler ) RefreshLinkedAccount ( ctx context . Context , rs * cstypes . RemoteSource , userName string , la * cstypes . LinkedAccount ) ( * cstypes . LinkedAccount , error ) {
2019-04-29 13:42:10 +00:00
switch rs . AuthType {
2019-07-31 13:17:54 +00:00
case cstypes . RemoteSourceAuthTypeOauth2 :
2019-04-29 13:42:10 +00:00
// refresh access token if expired
if isAccessTokenExpired ( la . Oauth2AccessTokenExpiresAt ) {
2022-02-24 10:18:29 +00:00
userSource , err := scommon . GetOauth2Source ( rs , "" )
2019-04-29 13:42:10 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-04-29 13:42:10 +00:00
}
token , err := userSource . RefreshOauth2Token ( la . Oauth2RefreshToken )
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-04-29 13:42:10 +00:00
}
if la . Oauth2AccessToken != token . AccessToken {
la . Oauth2AccessToken = token . AccessToken
la . Oauth2RefreshToken = token . RefreshToken
la . Oauth2AccessTokenExpiresAt = token . Expiry
2019-05-03 21:48:49 +00:00
if err := h . UpdateUserLA ( ctx , userName , la ) ; err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . Wrapf ( err , "failed to update linked account" )
2019-04-29 13:42:10 +00:00
}
}
}
}
return la , nil
}
// GetGitSource is a wrapper around common.GetGitSource that will also refresh
// the oauth2 access token and update the linked account when needed
2019-07-31 13:17:54 +00:00
func ( h * ActionHandler ) GetGitSource ( ctx context . Context , rs * cstypes . RemoteSource , userName string , la * cstypes . LinkedAccount ) ( gitsource . GitSource , error ) {
2019-05-03 21:48:49 +00:00
la , err := h . RefreshLinkedAccount ( ctx , rs , userName , la )
2019-04-29 13:42:10 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-04-29 13:42:10 +00:00
}
2022-02-22 14:01:29 +00:00
gs , err := scommon . GetGitSource ( rs , la )
return gs , errors . WithStack ( err )
2019-04-29 13:42:10 +00:00
}
2019-03-29 16:53:15 +00:00
type RegisterUserRequest struct {
2019-04-29 12:57:07 +00:00
UserName string
RemoteSourceName string
UserAccessToken string
Oauth2AccessToken string
Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time . Time
2019-03-29 16:53:15 +00:00
}
2019-07-31 13:17:54 +00:00
func ( h * ActionHandler ) RegisterUser ( ctx context . Context , req * RegisterUserRequest ) ( * cstypes . User , error ) {
2019-03-29 16:53:15 +00:00
if req . UserName == "" {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "user name required" ) )
2019-03-29 16:53:15 +00:00
}
if ! util . ValidateName ( req . UserName ) {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "invalid user name %q" , req . UserName ) )
2019-03-29 16:53:15 +00:00
}
2022-02-21 11:19:55 +00:00
rs , _ , err := h . configstoreClient . GetRemoteSource ( ctx , req . RemoteSourceName )
2019-03-29 16:53:15 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get remote source %q" , req . RemoteSourceName ) )
2019-03-29 16:53:15 +00:00
}
2019-07-05 11:33:29 +00:00
if ! * rs . RegistrationEnabled {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "remote source user registration is disabled" ) )
2019-07-05 11:33:29 +00:00
}
2019-03-29 16:53:15 +00:00
2022-02-24 10:18:29 +00:00
accessToken , err := scommon . GetAccessToken ( rs , req . UserAccessToken , req . Oauth2AccessToken )
2019-03-29 16:53:15 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-03-29 16:53:15 +00:00
}
2022-02-24 10:18:29 +00:00
userSource , err := scommon . GetUserSource ( rs , accessToken )
2019-03-29 16:53:15 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-03-29 16:53:15 +00:00
}
remoteUserInfo , err := userSource . GetUserInfo ( )
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . Wrapf ( err , "failed to retrieve remote user info for remote source %q" , rs . ID )
2019-03-29 16:53:15 +00:00
}
if remoteUserInfo . ID == "" {
return nil , errors . Errorf ( "empty remote user id for remote source %q" , rs . ID )
}
2019-07-31 13:39:07 +00:00
creq := & csapitypes . CreateUserRequest {
2019-03-29 16:53:15 +00:00
UserName : req . UserName ,
2019-07-31 13:39:07 +00:00
CreateUserLARequest : & csapitypes . CreateUserLARequest {
2019-04-29 12:57:07 +00:00
RemoteSourceName : req . RemoteSourceName ,
RemoteUserID : remoteUserInfo . ID ,
RemoteUserName : remoteUserInfo . LoginName ,
UserAccessToken : req . UserAccessToken ,
Oauth2AccessToken : req . Oauth2AccessToken ,
Oauth2RefreshToken : req . Oauth2RefreshToken ,
Oauth2AccessTokenExpiresAt : req . Oauth2AccessTokenExpiresAt ,
2019-03-29 16:53:15 +00:00
} ,
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "creating user account" )
2022-02-21 11:19:55 +00:00
u , _ , err := h . configstoreClient . CreateUser ( ctx , creq )
2019-03-29 16:53:15 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to create linked account" ) )
2019-03-29 16:53:15 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "user %q created" , req . UserName )
2019-03-29 16:53:15 +00:00
return u , nil
}
2019-02-21 16:58:25 +00:00
type LoginUserRequest struct {
2019-04-29 12:57:07 +00:00
RemoteSourceName string
UserAccessToken string
Oauth2AccessToken string
Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time . Time
2019-02-21 16:58:25 +00:00
}
2019-03-29 16:53:15 +00:00
2019-02-21 16:58:25 +00:00
type LoginUserResponse struct {
Token string
2019-07-31 13:17:54 +00:00
User * cstypes . User
2019-02-21 16:58:25 +00:00
}
2019-05-03 21:48:49 +00:00
func ( h * ActionHandler ) LoginUser ( ctx context . Context , req * LoginUserRequest ) ( * LoginUserResponse , error ) {
2022-02-21 11:19:55 +00:00
rs , _ , err := h . configstoreClient . GetRemoteSource ( ctx , req . RemoteSourceName )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get remote source %q" , req . RemoteSourceName ) )
2019-02-21 16:58:25 +00:00
}
2019-07-05 11:33:29 +00:00
if ! * rs . LoginEnabled {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "remote source user login is disabled" ) )
2019-07-05 11:33:29 +00:00
}
2019-02-21 16:58:25 +00:00
2022-02-24 10:18:29 +00:00
accessToken , err := scommon . GetAccessToken ( rs , req . UserAccessToken , req . Oauth2AccessToken )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
2022-02-24 10:18:29 +00:00
userSource , err := scommon . GetUserSource ( rs , accessToken )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
remoteUserInfo , err := userSource . GetUserInfo ( )
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . Wrapf ( err , "failed to retrieve remote user info for remote source %q" , rs . ID )
2019-02-21 16:58:25 +00:00
}
if remoteUserInfo . ID == "" {
return nil , errors . Errorf ( "empty remote user id for remote source %q" , rs . ID )
}
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUserByLinkedAccountRemoteUserAndSource ( ctx , remoteUserInfo . ID , rs . ID )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get user for remote user id %q and remote source %q" , remoteUserInfo . ID , rs . ID ) )
2019-02-21 16:58:25 +00:00
}
2019-07-31 13:17:54 +00:00
var la * cstypes . LinkedAccount
2019-02-21 16:58:25 +00:00
for _ , v := range user . LinkedAccounts {
if v . RemoteSourceID == rs . ID {
la = v
break
}
}
if la == nil {
2019-04-30 10:56:43 +00:00
return nil , errors . Errorf ( "linked account for user %q for remote source %q doesn't exist" , user . Name , rs . Name )
2019-02-21 16:58:25 +00:00
}
// Update oauth tokens if they have changed since the getuserinfo request may have updated them
2019-04-29 12:57:07 +00:00
if la . Oauth2AccessToken != req . Oauth2AccessToken ||
la . Oauth2RefreshToken != req . Oauth2RefreshToken ||
la . UserAccessToken != req . UserAccessToken {
2019-02-21 16:58:25 +00:00
2019-04-29 12:57:07 +00:00
la . Oauth2AccessToken = req . Oauth2AccessToken
la . Oauth2RefreshToken = req . Oauth2RefreshToken
la . UserAccessToken = req . UserAccessToken
2019-02-21 16:58:25 +00:00
2019-07-31 13:39:07 +00:00
creq := & csapitypes . UpdateUserLARequest {
2019-04-29 12:57:07 +00:00
RemoteUserID : la . RemoteUserID ,
RemoteUserName : la . RemoteUserName ,
UserAccessToken : la . UserAccessToken ,
Oauth2AccessToken : la . Oauth2AccessToken ,
Oauth2RefreshToken : la . Oauth2RefreshToken ,
Oauth2AccessTokenExpiresAt : la . Oauth2AccessTokenExpiresAt ,
2019-02-21 16:58:25 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "updating user %q linked account" , user . Name )
2022-02-21 11:19:55 +00:00
la , _ , err = h . configstoreClient . UpdateUserLA ( ctx , user . Name , la . ID , creq )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to update user" ) )
2019-02-21 16:58:25 +00:00
}
2022-02-21 17:07:58 +00:00
h . log . Info ( ) . Msgf ( "linked account %q for user %q updated" , la . ID , user . Name )
2019-02-21 16:58:25 +00:00
}
// generate jwt token
2022-02-24 10:18:29 +00:00
token , err := scommon . GenerateLoginJWTToken ( h . sd , user . ID )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
return & LoginUserResponse {
Token : token ,
User : user ,
} , nil
}
2019-03-29 16:53:15 +00:00
type AuthorizeRequest struct {
2019-04-29 12:57:07 +00:00
RemoteSourceName string
UserAccessToken string
Oauth2AccessToken string
Oauth2RefreshToken string
Oauth2AccessTokenExpiresAt time . Time
2019-03-29 16:53:15 +00:00
}
type AuthorizeResponse struct {
RemoteUserInfo * gitsource . UserInfo
RemoteSourceName string
}
2019-05-03 21:48:49 +00:00
func ( h * ActionHandler ) Authorize ( ctx context . Context , req * AuthorizeRequest ) ( * AuthorizeResponse , error ) {
2022-02-21 11:19:55 +00:00
rs , _ , err := h . configstoreClient . GetRemoteSource ( ctx , req . RemoteSourceName )
2019-03-29 16:53:15 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get remote source %q" , req . RemoteSourceName ) )
2019-03-29 16:53:15 +00:00
}
2022-02-24 10:18:29 +00:00
accessToken , err := scommon . GetAccessToken ( rs , req . UserAccessToken , req . Oauth2AccessToken )
2019-03-29 16:53:15 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-03-29 16:53:15 +00:00
}
2022-02-24 10:18:29 +00:00
userSource , err := scommon . GetUserSource ( rs , accessToken )
2019-03-29 16:53:15 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-03-29 16:53:15 +00:00
}
remoteUserInfo , err := userSource . GetUserInfo ( )
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . Wrapf ( err , "failed to retrieve remote user info for remote source %q" , rs . ID )
2019-03-29 16:53:15 +00:00
}
if remoteUserInfo . ID == "" {
return nil , errors . Errorf ( "empty remote user id for remote source %q" , rs . ID )
}
return & AuthorizeResponse {
RemoteUserInfo : remoteUserInfo ,
RemoteSourceName : req . RemoteSourceName ,
} , nil
}
2019-02-21 16:58:25 +00:00
type RemoteSourceAuthResponse struct {
Oauth2Redirect string
Response interface { }
}
2019-05-03 21:48:49 +00:00
func ( h * ActionHandler ) HandleRemoteSourceAuth ( ctx context . Context , remoteSourceName , loginName , loginPassword string , requestType RemoteSourceRequestType , req interface { } ) ( * RemoteSourceAuthResponse , error ) {
2022-02-21 11:19:55 +00:00
rs , _ , err := h . configstoreClient . GetRemoteSource ( ctx , remoteSourceName )
2019-03-29 13:33:41 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get remote source %q" , remoteSourceName ) )
2019-03-29 13:33:41 +00:00
}
switch requestType {
2019-03-29 16:53:15 +00:00
case RemoteSourceRequestTypeCreateUserLA :
2019-03-29 13:33:41 +00:00
req := req . ( * CreateUserLARequest )
2019-05-03 21:19:23 +00:00
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUser ( ctx , req . UserRef )
2019-03-29 13:33:41 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get user %q" , req . UserRef ) )
2019-03-29 13:33:41 +00:00
}
2019-05-03 21:19:23 +00:00
2022-02-24 10:18:29 +00:00
curUserID := common . CurrentUserID ( ctx )
2019-05-03 21:19:23 +00:00
// user must be already logged in the create a linked account and can create a
// linked account only on itself.
if user . ID != curUserID {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "logged in user cannot create linked account for another user" ) )
2019-05-03 21:19:23 +00:00
}
2019-07-31 13:17:54 +00:00
var la * cstypes . LinkedAccount
2019-03-29 13:33:41 +00:00
for _ , v := range user . LinkedAccounts {
if v . RemoteSourceID == rs . ID {
la = v
break
}
}
if la != nil {
2022-02-21 11:19:55 +00:00
return nil , util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "user %q already have a linked account for remote source %q" , req . UserRef , rs . Name ) )
2019-03-29 13:33:41 +00:00
}
2019-03-29 16:53:15 +00:00
case RemoteSourceRequestTypeLoginUser :
case RemoteSourceRequestTypeAuthorize :
case RemoteSourceRequestTypeRegisterUser :
2019-03-29 13:33:41 +00:00
default :
return nil , errors . Errorf ( "unknown request type: %q" , requestType )
}
2019-02-21 16:58:25 +00:00
switch rs . AuthType {
2019-07-31 13:17:54 +00:00
case cstypes . RemoteSourceAuthTypeOauth2 :
2022-02-24 10:18:29 +00:00
oauth2Source , err := scommon . GetOauth2Source ( rs , "" )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . Wrapf ( err , "failed to create git source" )
2019-02-21 16:58:25 +00:00
}
2022-02-24 10:18:29 +00:00
token , err := scommon . GenerateOauth2JWTToken ( h . sd , rs . Name , string ( requestType ) , req )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
2019-05-03 21:48:49 +00:00
redirect , err := oauth2Source . GetOauth2AuthorizationURL ( h . webExposedURL + "/oauth2/callback" , token )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
return & RemoteSourceAuthResponse {
Oauth2Redirect : redirect ,
} , nil
2019-07-31 13:17:54 +00:00
case cstypes . RemoteSourceAuthTypePassword :
2022-02-24 10:18:29 +00:00
passwordSource , err := scommon . GetPasswordSource ( rs , "" )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . Wrapf ( err , "failed to create git source" )
2019-02-21 16:58:25 +00:00
}
2019-05-03 21:48:49 +00:00
tokenName := "agola-" + h . agolaID
2019-04-30 10:13:12 +00:00
accessToken , err := passwordSource . LoginPassword ( loginName , loginPassword , tokenName )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-24 11:56:52 +00:00
if errors . Is ( err , gitsource . ErrUnauthorized ) {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . ErrUnauthorized , errors . Wrapf ( err , "failed to login to remotesource %q" , remoteSourceName ) )
2019-05-13 12:23:41 +00:00
}
2022-02-22 14:01:29 +00:00
return nil , errors . Wrapf ( err , "failed to login to remote source %q with login name %q" , rs . Name , loginName )
2019-02-21 16:58:25 +00:00
}
requestj , err := json . Marshal ( req )
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
2019-05-03 21:48:49 +00:00
cres , err := h . HandleRemoteSourceAuthRequest ( ctx , requestType , string ( requestj ) , accessToken , "" , "" , time . Time { } )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
return & RemoteSourceAuthResponse {
Response : cres . Response ,
} , nil
default :
return nil , errors . Errorf ( "unknown remote source authentication type: %q" , rs . AuthType )
}
}
2019-03-29 16:53:15 +00:00
type RemoteSourceRequestType string
const (
RemoteSourceRequestTypeCreateUserLA RemoteSourceRequestType = "createuserla"
RemoteSourceRequestTypeLoginUser RemoteSourceRequestType = "loginuser"
RemoteSourceRequestTypeAuthorize RemoteSourceRequestType = "authorize"
RemoteSourceRequestTypeRegisterUser RemoteSourceRequestType = "registeruser"
)
2019-02-21 16:58:25 +00:00
type RemoteSourceAuthResult struct {
2019-03-29 16:53:15 +00:00
RequestType RemoteSourceRequestType
2019-02-21 16:58:25 +00:00
Response interface { }
}
type CreateUserLAResponse struct {
2019-07-31 13:17:54 +00:00
LinkedAccount * cstypes . LinkedAccount
2019-02-21 16:58:25 +00:00
}
2019-05-03 21:48:49 +00:00
func ( h * ActionHandler ) HandleRemoteSourceAuthRequest ( ctx context . Context , requestType RemoteSourceRequestType , requestString string , userAccessToken , oauth2AccessToken , oauth2RefreshToken string , oauth2AccessTokenExpiresAt time . Time ) ( * RemoteSourceAuthResult , error ) {
2019-02-21 16:58:25 +00:00
switch requestType {
2019-03-29 16:53:15 +00:00
case RemoteSourceRequestTypeCreateUserLA :
2019-02-21 16:58:25 +00:00
var req * CreateUserLARequest
if err := json . Unmarshal ( [ ] byte ( requestString ) , & req ) ; err != nil {
return nil , errors . Errorf ( "failed to unmarshal request" )
}
creq := & CreateUserLARequest {
2019-05-03 09:07:53 +00:00
UserRef : req . UserRef ,
2019-04-29 12:57:07 +00:00
RemoteSourceName : req . RemoteSourceName ,
UserAccessToken : userAccessToken ,
Oauth2AccessToken : oauth2AccessToken ,
Oauth2RefreshToken : oauth2RefreshToken ,
Oauth2AccessTokenExpiresAt : oauth2AccessTokenExpiresAt ,
2019-02-21 16:58:25 +00:00
}
2019-05-03 21:48:49 +00:00
la , err := h . CreateUserLA ( ctx , creq )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
return & RemoteSourceAuthResult {
RequestType : requestType ,
Response : & CreateUserLAResponse {
LinkedAccount : la ,
} ,
} , nil
2019-04-29 12:57:07 +00:00
case RemoteSourceRequestTypeRegisterUser :
var req * RegisterUserRequest
2019-02-21 16:58:25 +00:00
if err := json . Unmarshal ( [ ] byte ( requestString ) , & req ) ; err != nil {
return nil , errors . Errorf ( "failed to unmarshal request" )
}
2019-04-29 12:57:07 +00:00
creq := & RegisterUserRequest {
UserName : req . UserName ,
RemoteSourceName : req . RemoteSourceName ,
UserAccessToken : userAccessToken ,
Oauth2AccessToken : oauth2AccessToken ,
Oauth2RefreshToken : oauth2RefreshToken ,
Oauth2AccessTokenExpiresAt : oauth2AccessTokenExpiresAt ,
2019-02-21 16:58:25 +00:00
}
2019-05-03 21:48:49 +00:00
cresp , err := h . RegisterUser ( ctx , creq )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
return & RemoteSourceAuthResult {
RequestType : requestType ,
2019-03-29 16:53:15 +00:00
Response : cresp ,
} , nil
2019-04-29 12:57:07 +00:00
case RemoteSourceRequestTypeLoginUser :
var req * LoginUserRequest
2019-03-29 16:53:15 +00:00
if err := json . Unmarshal ( [ ] byte ( requestString ) , & req ) ; err != nil {
return nil , errors . Errorf ( "failed to unmarshal request" )
}
2019-04-29 12:57:07 +00:00
creq := & LoginUserRequest {
RemoteSourceName : req . RemoteSourceName ,
UserAccessToken : userAccessToken ,
Oauth2AccessToken : oauth2AccessToken ,
Oauth2RefreshToken : oauth2RefreshToken ,
Oauth2AccessTokenExpiresAt : oauth2AccessTokenExpiresAt ,
2019-03-29 16:53:15 +00:00
}
2019-05-03 21:48:49 +00:00
cresp , err := h . LoginUser ( ctx , creq )
2019-03-29 16:53:15 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-03-29 16:53:15 +00:00
}
return & RemoteSourceAuthResult {
RequestType : requestType ,
Response : cresp ,
} , nil
2019-04-29 12:57:07 +00:00
case RemoteSourceRequestTypeAuthorize :
var req * AuthorizeRequest
2019-03-29 16:53:15 +00:00
if err := json . Unmarshal ( [ ] byte ( requestString ) , & req ) ; err != nil {
return nil , errors . Errorf ( "failed to unmarshal request" )
}
2019-04-29 12:57:07 +00:00
creq := & AuthorizeRequest {
RemoteSourceName : req . RemoteSourceName ,
UserAccessToken : userAccessToken ,
Oauth2AccessToken : oauth2AccessToken ,
Oauth2RefreshToken : oauth2RefreshToken ,
Oauth2AccessTokenExpiresAt : oauth2AccessTokenExpiresAt ,
2019-03-29 16:53:15 +00:00
}
2019-05-03 21:48:49 +00:00
cresp , err := h . Authorize ( ctx , creq )
2019-03-29 16:53:15 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-03-29 16:53:15 +00:00
}
return & RemoteSourceAuthResult {
RequestType : requestType ,
Response : cresp ,
2019-02-21 16:58:25 +00:00
} , nil
default :
return nil , errors . Errorf ( "unknown request" )
}
}
2019-05-03 21:48:49 +00:00
func ( h * ActionHandler ) HandleOauth2Callback ( ctx context . Context , code , state string ) ( * RemoteSourceAuthResult , error ) {
2019-02-21 16:58:25 +00:00
token , err := jwt . Parse ( state , func ( token * jwt . Token ) ( interface { } , error ) {
2019-05-03 21:48:49 +00:00
if token . Method != h . sd . Method {
2019-02-21 16:58:25 +00:00
return nil , errors . Errorf ( "unexpected signing method: %v" , token . Header [ "alg" ] )
}
var key interface { }
2019-05-03 21:48:49 +00:00
switch h . sd . Method {
2019-02-21 16:58:25 +00:00
case jwt . SigningMethodRS256 :
2019-05-03 21:48:49 +00:00
key = h . sd . PrivateKey
2019-02-21 16:58:25 +00:00
case jwt . SigningMethodHS256 :
2019-05-03 21:48:49 +00:00
key = h . sd . Key
2019-02-21 16:58:25 +00:00
default :
2019-05-03 21:48:49 +00:00
return nil , errors . Errorf ( "unsupported signing method %q" , h . sd . Method . Alg ( ) )
2019-02-21 16:58:25 +00:00
}
return key , nil
} )
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . Wrapf ( err , "failed to parse jwt" )
2019-02-21 16:58:25 +00:00
}
if ! token . Valid {
return nil , errors . Errorf ( "invalid token" )
}
claims := token . Claims . ( jwt . MapClaims )
remoteSourceName := claims [ "remote_source_name" ] . ( string )
2019-03-29 16:53:15 +00:00
requestType := RemoteSourceRequestType ( claims [ "request_type" ] . ( string ) )
2019-02-21 16:58:25 +00:00
requestString := claims [ "request" ] . ( string )
2022-02-21 11:19:55 +00:00
rs , _ , err := h . configstoreClient . GetRemoteSource ( ctx , remoteSourceName )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get remote source %q" , remoteSourceName ) )
2019-02-21 16:58:25 +00:00
}
2022-02-24 10:18:29 +00:00
oauth2Source , err := scommon . GetOauth2Source ( rs , "" )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . Wrapf ( err , "failed to create oauth2 source" )
2019-02-21 16:58:25 +00:00
}
2019-05-03 21:48:49 +00:00
oauth2Token , err := oauth2Source . RequestOauth2Token ( h . webExposedURL + "/oauth2/callback" , code )
2019-02-21 16:58:25 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return nil , errors . WithStack ( err )
2019-02-21 16:58:25 +00:00
}
2019-05-03 21:48:49 +00:00
return h . HandleRemoteSourceAuthRequest ( ctx , requestType , requestString , "" , oauth2Token . AccessToken , oauth2Token . RefreshToken , oauth2Token . Expiry )
2019-02-21 16:58:25 +00:00
}
2019-05-05 12:45:19 +00:00
func ( h * ActionHandler ) DeleteUser ( ctx context . Context , userRef string ) error {
2022-02-24 10:18:29 +00:00
if ! common . IsUserAdmin ( ctx ) {
2019-05-03 21:19:23 +00:00
return errors . Errorf ( "user not logged in" )
}
2022-02-21 11:19:55 +00:00
if _ , err := h . configstoreClient . DeleteUser ( ctx , userRef ) ; err != nil {
2022-02-22 14:01:29 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to delete user" ) )
2019-05-05 12:45:19 +00:00
}
return nil
}
func ( h * ActionHandler ) DeleteUserLA ( ctx context . Context , userRef , laID string ) error {
2022-02-24 10:18:29 +00:00
if ! common . IsUserLoggedOrAdmin ( ctx ) {
2019-05-03 21:19:23 +00:00
return errors . Errorf ( "user not logged in" )
}
2022-02-24 10:18:29 +00:00
isAdmin := common . IsUserAdmin ( ctx )
curUserID := common . CurrentUserID ( ctx )
2019-05-03 21:19:23 +00:00
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUser ( ctx , userRef )
2019-05-03 21:19:23 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get user %q" , userRef ) )
2019-05-03 21:19:23 +00:00
}
// only admin or the same logged user can create a token
if ! isAdmin && user . ID != curUserID {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "logged in user cannot create token for another user" ) )
2019-05-03 21:19:23 +00:00
}
2022-02-21 11:19:55 +00:00
if _ , err = h . configstoreClient . DeleteUserLA ( ctx , userRef , laID ) ; err != nil {
2022-02-22 14:01:29 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to delete user linked account" ) )
2019-05-05 12:45:19 +00:00
}
return nil
}
func ( h * ActionHandler ) DeleteUserToken ( ctx context . Context , userRef , tokenName string ) error {
2022-02-24 10:18:29 +00:00
if ! common . IsUserLoggedOrAdmin ( ctx ) {
2019-05-03 21:19:23 +00:00
return errors . Errorf ( "user not logged in" )
}
2022-02-24 10:18:29 +00:00
isAdmin := common . IsUserAdmin ( ctx )
curUserID := common . CurrentUserID ( ctx )
2019-05-03 21:19:23 +00:00
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUser ( ctx , userRef )
2019-05-03 21:19:23 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get user %q" , userRef ) )
2019-05-03 21:19:23 +00:00
}
// only admin or the same logged user can create a token
if ! isAdmin && user . ID != curUserID {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "logged in user cannot delete token for another user" ) )
2019-05-03 21:19:23 +00:00
}
2022-02-21 11:19:55 +00:00
if _ , err = h . configstoreClient . DeleteUserToken ( ctx , userRef , tokenName ) ; err != nil {
2022-02-22 14:01:29 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to delete user token" ) )
2019-05-05 12:45:19 +00:00
}
return nil
}
2019-06-11 13:09:41 +00:00
2019-07-03 13:19:52 +00:00
type UserCreateRunRequest struct {
RepoUUID string
RepoPath string
Branch string
2019-08-02 08:03:28 +00:00
Tag string
Ref string
2019-07-03 13:19:52 +00:00
CommitSHA string
Message string
2019-08-02 08:03:28 +00:00
PullRequestRefRegexes [ ] string
2019-08-05 15:33:30 +00:00
Variables map [ string ] string
2019-07-03 13:19:52 +00:00
}
func ( h * ActionHandler ) UserCreateRun ( ctx context . Context , req * UserCreateRunRequest ) error {
2019-08-02 08:03:28 +00:00
prRefRegexes := [ ] * regexp . Regexp { }
for _ , res := range req . PullRequestRefRegexes {
re , err := regexp . Compile ( res )
if err != nil {
2022-02-22 14:01:29 +00:00
return errors . Wrapf ( err , "wrong regular expression %q" , res )
2019-08-02 08:03:28 +00:00
}
prRefRegexes = append ( prRefRegexes , re )
}
2022-02-24 10:18:29 +00:00
curUserID := common . CurrentUserID ( ctx )
2019-06-11 13:09:41 +00:00
2022-02-21 11:19:55 +00:00
user , _ , err := h . configstoreClient . GetUser ( ctx , curUserID )
2019-06-11 13:09:41 +00:00
if err != nil {
2022-02-22 14:01:29 +00:00
return util . NewAPIError ( util . KindFromRemoteError ( err ) , errors . Wrapf ( err , "failed to get user %q" , curUserID ) )
2019-06-11 13:09:41 +00:00
}
// Verify that the repo is owned by the user
2019-07-03 13:19:52 +00:00
repoParts := strings . Split ( req . RepoPath , "/" )
if req . RepoUUID == "" {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "empty repo uuid" ) )
2019-07-03 13:19:52 +00:00
}
2019-06-11 13:09:41 +00:00
if len ( repoParts ) != 2 {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "wrong repo path: %q" , req . RepoPath ) )
2019-06-11 13:09:41 +00:00
}
if repoParts [ 0 ] != user . ID {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrUnauthorized , errors . Errorf ( "repo %q not owned" , req . RepoPath ) )
2019-06-11 13:09:41 +00:00
}
2019-08-02 08:03:28 +00:00
branch := req . Branch
tag := req . Tag
ref := req . Ref
set := 0
if branch != "" {
set ++
}
if tag != "" {
set ++
}
if ref != "" {
set ++
}
if set == 0 {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "one of branch, tag or ref is required" ) )
2019-08-02 08:03:28 +00:00
}
if set > 1 {
2022-02-21 11:19:55 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Errorf ( "only one of branch, tag or ref can be provided" ) )
2019-08-02 08:03:28 +00:00
}
gitSource := agolagit . New ( h . apiExposedURL + "/repos" , prRefRegexes )
2019-07-03 13:19:52 +00:00
cloneURL := fmt . Sprintf ( "%s/%s.git" , h . apiExposedURL + "/repos" , req . RepoPath )
2019-06-11 13:09:41 +00:00
2019-08-02 08:03:28 +00:00
if ref == "" {
if branch != "" {
ref = gitSource . BranchRef ( branch )
}
if tag != "" {
ref = gitSource . TagRef ( tag )
}
}
gitRefType , name , err := gitSource . RefType ( ref )
if err != nil {
2022-02-22 14:01:29 +00:00
return util . NewAPIError ( util . ErrBadRequest , errors . Wrapf ( err , "failed to get refType for ref %q" , ref ) )
2019-08-02 08:03:28 +00:00
}
var pullRequestID string
switch gitRefType {
case gitsource . RefTypeBranch :
branch = name
case gitsource . RefTypeTag :
tag = name
case gitsource . RefTypePullRequest :
pullRequestID = name
default :
return errors . Errorf ( "unsupported ref %q for manual run creation" , ref )
}
var refType types . RunRefType
if branch != "" {
refType = types . RunRefTypeBranch
}
if tag != "" {
refType = types . RunRefTypeTag
}
if pullRequestID != "" {
refType = types . RunRefTypePullRequest
}
2019-07-03 13:19:52 +00:00
creq := & CreateRunRequest {
2019-06-11 13:09:41 +00:00
RunType : types . RunTypeUser ,
2019-08-02 08:03:28 +00:00
RefType : refType ,
2019-06-11 13:09:41 +00:00
RunCreationTrigger : types . RunCreationTriggerTypeManual ,
Project : nil ,
User : user ,
2019-07-03 13:19:52 +00:00
RepoPath : req . RepoPath ,
2019-06-11 13:09:41 +00:00
GitSource : gitSource ,
2019-07-03 13:19:52 +00:00
CommitSHA : req . CommitSHA ,
Message : req . Message ,
2019-08-02 08:03:28 +00:00
Branch : branch ,
Tag : tag ,
Ref : ref ,
PullRequestID : pullRequestID ,
2019-06-11 13:09:41 +00:00
CloneURL : cloneURL ,
CommitLink : "" ,
BranchLink : "" ,
TagLink : "" ,
PullRequestLink : "" ,
2019-07-03 13:19:52 +00:00
UserRunRepoUUID : req . RepoUUID ,
2019-08-05 15:33:30 +00:00
Variables : req . Variables ,
2019-06-11 13:09:41 +00:00
}
2019-07-03 13:19:52 +00:00
return h . CreateRuns ( ctx , creq )
2019-06-11 13:09:41 +00:00
}