Skip to content

Commit

Permalink
Merge pull request #1167 from npamudika/4.2.x
Browse files Browse the repository at this point in the history
Add changes to support PAT
  • Loading branch information
npamudika authored May 29, 2024
2 parents b39f95b + cf9db35 commit 48aa1f5
Show file tree
Hide file tree
Showing 20 changed files with 153 additions and 103 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
run: |
cd import-export-cli
go vet ./...
./build.sh -t apictl.go -v 4.2.4
./build.sh -t apictl.go -v 4.2.5
- name: Start APIM
run: |
Expand All @@ -69,4 +69,4 @@ jobs:
- name: Test
run: |
cd import-export-cli/integration
go test -p 1 -timeout 0 -archive apictl-4.2.4-linux-amd64.tar.gz -race -coverprofile=coverage.txt -covermode=atomic ./pkg/...
go test -p 1 -timeout 0 -archive apictl-4.2.5-linux-amd64.tar.gz -race -coverprofile=coverage.txt -covermode=atomic ./pkg/...
6 changes: 3 additions & 3 deletions import-export-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Command Line tool for importing and exporting APIs/Applications/API Products in
- ### Building
`cd` into `product-apim-tooling/import-export-cli`

Execute `./build.sh -t apictl.go -v 4.2.4 -f` to build for all platforms.
Execute `./build.sh -t apictl.go -v 4.2.5 -f` to build for all platforms.

Created packages will be available at `build/target` directory

Expand Down Expand Up @@ -69,11 +69,11 @@ Command Line tool for importing and exporting APIs/Applications/API Products in

Usage: `docker build --build-arg version=<version> -t apictl:<version> .`

Example: `docker build --build-arg version=4.2.4.1 -t apictl:4.2.4.1 .`
Example: `docker build --build-arg version=4.2.5.1 -t apictl:4.2.5.1 .`

- ### Using the Docker Image

`docker run -it -v $(pwd):/git -v ~/.wso2apictl:/root/.wso2apictl -v ~/.wso2apictl.local:/root/.wso2apictl.local apictl:4.2.4.1 <apictl command>`
`docker run -it -v $(pwd):/git -v ~/.wso2apictl:/root/.wso2apictl -v ~/.wso2apictl.local:/root/.wso2apictl.local apictl:4.2.5.1 <apictl command>`

***

Expand Down
1 change: 1 addition & 0 deletions import-export-cli/cmd/deprecated/listApiProducts.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func executeApiProductsCmd(credential credentials.Credential) {
impl.PrintAPIProducts(apiProducts, listApiProductsCmdFormat)
} else {
utils.Logln(utils.LogPrefixError+"Getting List of API Products", err)
utils.HandleErrorAndExit("Error getting the list of API Products.", err)
}
}

Expand Down
1 change: 1 addition & 0 deletions import-export-cli/cmd/deprecated/listApis.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func executeApisCmd(credential credentials.Credential) {
impl.PrintAPIs(apis, listApisCmdFormat)
} else {
utils.Logln(utils.LogPrefixError+"Getting List of APIs", err)
utils.HandleErrorAndExit("Error getting the list of APIs.", err)
}
}

Expand Down
1 change: 1 addition & 0 deletions import-export-cli/cmd/deprecated/listApps.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func executeAppsCmd(credential credentials.Credential, appOwner string) {
impl.PrintApps(apps, listAppsCmdFormat)
} else {
utils.Logln(utils.LogPrefixError+"Getting List of Applications", err)
utils.HandleErrorAndExit("Error getting the list of Applications.", err)
}
}

Expand Down
1 change: 1 addition & 0 deletions import-export-cli/cmd/getAPIProductRevisions.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func executeGetAPIProductRevisionsCmd(credential credentials.Credential) {
impl.PrintRevisions(revisions, getAPIProductRevisionsCmdFormat)
} else {
utils.Logln(utils.LogPrefixError+"Getting List of Revisions", err)
utils.HandleErrorAndExit("Error getting the list of revisions.", err)
}
}

Expand Down
1 change: 1 addition & 0 deletions import-export-cli/cmd/getAPIRevisions.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func executeGetAPIRevisionsCmd(credential credentials.Credential) {
impl.PrintRevisions(revisions, getAPIRevisionsCmdFormat)
} else {
utils.Logln(utils.LogPrefixError+"Getting List of API Revisions", err)
utils.HandleErrorAndExit("Error getting the list of API revisions.", err)
}
}

Expand Down
1 change: 1 addition & 0 deletions import-export-cli/cmd/getApiProducts.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func executeGetApiProductsCmd(credential credentials.Credential) {
impl.PrintAPIProducts(apiProducts, getApiProductsCmdFormat)
} else {
utils.Logln(utils.LogPrefixError+"Getting List of API Products", err)
utils.HandleErrorAndExit("Error getting the list of API Products.", err)
}
}

Expand Down
1 change: 1 addition & 0 deletions import-export-cli/cmd/getApis.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func executeGetApisCmd(credential credentials.Credential) {
impl.PrintAPIs(apis, getApisCmdFormat)
} else {
utils.Logln(utils.LogPrefixError+"Getting List of APIs", err)
utils.HandleErrorAndExit("Error getting the list of APIs.", err)
}
}

Expand Down
1 change: 1 addition & 0 deletions import-export-cli/cmd/getApps.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func executeGetAppsCmd(credential credentials.Credential, appOwner string) {
impl.PrintApps(apps, getAppsCmdFormat)
} else {
utils.Logln(utils.LogPrefixError+"Getting List of Applications", err)
utils.HandleErrorAndExit("Error getting the list of Applications.", err)
}
}

Expand Down
95 changes: 55 additions & 40 deletions import-export-cli/cmd/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ import (
var loginUsername string
var loginPassword string
var loginPasswordStdin bool
var clientId string
var clientSecret string
var personalAccessToken string

const loginCmdLiteral = "login [environment] [flags]"
const loginCmdShortDesc = "Login to an API Manager"
const loginCmdLongDesc = `Login to an API Manager using credentials`
const loginCmdLongDesc = `Login to an API Manager using credentials or set token for authentication`
const loginCmdExamples = utils.ProjectName + " login dev -u admin -p admin\n" +
utils.ProjectName + " login dev -u admin\n" +
"cat ~/.mypassword | " + utils.ProjectName + " login dev -u admin"
"cat ~/.mypassword | " + utils.ProjectName + " login dev -u admin\n" +
utils.ProjectName + " login dev --token e79bda48-3406-3178-acce-f6e4dbdcbb12"

// loginCmd represents the login command
var loginCmd = &cobra.Command{
Expand All @@ -52,58 +56,64 @@ var loginCmd = &cobra.Command{
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
environment := args[0]

if loginPassword != "" {
fmt.Println("Warning: Using --password in CLI is not secure. Use --password-stdin")
if loginPasswordStdin {
fmt.Println("--password and --password-stdin are mutual exclusive")
os.Exit(1)
}
store, err := credentials.GetDefaultCredentialStore()
if err != nil {
fmt.Println("Error occurred while loading credential store : ", err)
os.Exit(1)
}

if loginPasswordStdin {
if loginUsername == "" {
fmt.Println("An username is required to use password-stdin")
if personalAccessToken != "" {
err = runLogin(store, environment, loginUsername, loginPassword, personalAccessToken)
if err != nil {
fmt.Println("Error occurred while login using the token : ", err)
os.Exit(1)
}
} else {
if loginPassword != "" {
fmt.Println("Warning: Using --password in CLI is not secure. Use --password-stdin")
if loginPasswordStdin {
fmt.Println("--password and --password-stdin are mutual exclusive")
os.Exit(1)
}
}

data, err := ioutil.ReadAll(os.Stdin)
if loginPasswordStdin {
if loginUsername == "" {
fmt.Println("An username is required to use password-stdin")
os.Exit(1)
}

data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

loginPassword = strings.TrimRight(strings.TrimSuffix(string(data), "\n"), "\r")
}
err = runLogin(store, environment, loginUsername, loginPassword, personalAccessToken)
if err != nil {
fmt.Println(err)
fmt.Println("Error occurred while login : ", err)
os.Exit(1)
}

loginPassword = strings.TrimRight(strings.TrimSuffix(string(data), "\n"), "\r")
}

store, err := credentials.GetDefaultCredentialStore()
if err != nil {
fmt.Println("Error occurred while loading credential store : ", err)
os.Exit(1)
}
err = runLogin(store, environment, loginUsername, loginPassword)
if err != nil {
fmt.Println("Error occurred while login : ", err)
os.Exit(1)
}
},
}

func runLogin(store credentials.Store, environment, username, password string) error {
func runLogin(store credentials.Store, environment, username, password, personalAccessToken string) error {
if !utils.APIMExistsInEnv(environment, utils.MainConfigFilePath) {
fmt.Println("APIM does not exists in", environment, "Add it using add env")
os.Exit(1)
}

if username == "" {
if username == "" && personalAccessToken == "" {
fmt.Print("Username:")
scanner := bufio.NewScanner(os.Stdin)
if scanner.Scan() {
username = scanner.Text()
}
}

if password == "" {
if password == "" && personalAccessToken == "" {
fmt.Print("Password:")
pass, err := terminal.ReadPassword(int(syscall.Stdin))
if err != nil {
Expand All @@ -113,22 +123,26 @@ func runLogin(store credentials.Store, environment, username, password string) e
fmt.Println()
}

registrationEndpoint := utils.GetRegistrationEndpointOfEnv(environment, utils.MainConfigFilePath)
clientId, clientSecret, err := utils.GetClientIDSecret(username, password, registrationEndpoint)
if err != nil {
return err
if username != "" && password != "" {
registrationEndpoint := utils.GetRegistrationEndpointOfEnv(environment, utils.MainConfigFilePath)
id, secret, err := utils.GetClientIDSecret(username, password, registrationEndpoint)
if err != nil {
return err
}
clientId = id
clientSecret = secret
}

fmt.Println("Logged into APIM in", environment, "environment")
err = store.SetAPIMCredentials(environment, username, password, clientId, clientSecret)
fmt.Println("Logged into APIM in ", environment, "environment")
err := store.SetAPIMCredentials(environment, username, password, clientId, clientSecret, personalAccessToken)
if err != nil {
return err
}

return nil
}

// GetCredentials functions get the credentials for the specified environment
// GetCredentials function gets the credentials for the specified environment
func GetCredentials(env string) (credentials.Credential, error) {
// get tokens or login
store, err := credentials.GetDefaultCredentialStore()
Expand All @@ -143,8 +157,8 @@ func GetCredentials(env string) (credentials.Credential, error) {

// check for creds
if !store.HasAPIM(env) {
fmt.Println("Login to APIM in", env)
err = runLogin(store, env, "", "")
fmt.Println("Login to APIM in ", env)
err = runLogin(store, env, "", "", "")
if err != nil {
return credentials.Credential{}, err
}
Expand All @@ -164,4 +178,5 @@ func init() {
loginCmd.Flags().StringVarP(&loginUsername, "username", "u", "", "Username for login")
loginCmd.Flags().StringVarP(&loginPassword, "password", "p", "", "Password for login")
loginCmd.Flags().BoolVarP(&loginPasswordStdin, "password-stdin", "", false, "Get password from stdin")
loginCmd.Flags().StringVarP(&personalAccessToken, "token", "", "", "Personal access token")
}
85 changes: 47 additions & 38 deletions import-export-cli/credentials/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type Credential struct {
ClientId string `json:"clientId"`
// ClientSecret for cli
ClientSecret string `json:"clientSecret"`
// PersonalAccessToken of API Manager
PersonalAccessToken string `json:"accessToken"`
}

// Credentials of cli
Expand Down Expand Up @@ -83,15 +85,19 @@ func GetDefaultCredentialStore() (Store, error) {

// GetOAuthAccessToken generates an accesstoken for CLI
func GetOAuthAccessToken(credential Credential, env string) (string, error) {
tokenEndpoint := utils.GetInternalTokenEndpointOfEnv(env, utils.MainConfigFilePath)
data, err := utils.GetOAuthTokens(credential.Username, credential.Password,
Base64Encode(credential.ClientId+":"+credential.ClientSecret),
tokenEndpoint)
if err != nil {
return "", err
}
if accessToken, ok := data["access_token"]; ok {
return accessToken, nil
if credential.PersonalAccessToken != "" {
return credential.PersonalAccessToken, nil
} else {
tokenEndpoint := utils.GetInternalTokenEndpointOfEnv(env, utils.MainConfigFilePath)
data, err := utils.GetOAuthTokens(credential.Username, credential.Password,
Base64Encode(credential.ClientId+":"+credential.ClientSecret),
tokenEndpoint)
if err != nil {
return "", err
}
if accessToken, ok := data["access_token"]; ok {
return accessToken, nil
}
}
return "", errors.New("access_token not found")
}
Expand All @@ -101,35 +107,38 @@ func GetBasicAuth(credential Credential) string {
return Base64Encode(fmt.Sprintf("%s:%s", credential.Username, credential.Password))
}

//Revoke access Token when user is logging out from environment
// Revoke access Token when user is logging out from environment
func RevokeAccessToken(credential Credential, env string, token string) error {

//get revoke endpoint
tokenRevokeEndpoint := utils.GetTokenRevokeEndpoint(env, utils.MainConfigFilePath)
//Encoding client secret and client Id
var b64EncodedClientIDClientSecret = utils.GetBase64EncodedCredentials(credential.ClientId, credential.ClientSecret)
// set headers to request
headers := make(map[string]string)
headers[utils.HeaderContentType] = utils.HeaderValueXWWWFormUrlEncoded
headers[utils.HeaderAuthorization] = utils.HeaderValueAuthBasicPrefix + " " + b64EncodedClientIDClientSecret

//Create body for the request
body := utils.HeaderToken + token + utils.TokenTypeForRevocation

utils.Logln(utils.LogPrefixInfo + "connecting to " + tokenRevokeEndpoint)
resp, err := utils.InvokePOSTRequest(tokenRevokeEndpoint, headers, body)

if err != nil {
return err
}

//Check status code
if resp.StatusCode() != http.StatusOK {
return errors.New("Request didn't respond 200 OK for searching token revocation " +
"Status: " + resp.Status())
if credential.PersonalAccessToken != "" {
return nil
} else {
//get revoke endpoint
tokenRevokeEndpoint := utils.GetTokenRevokeEndpoint(env, utils.MainConfigFilePath)
//Encoding client secret and client Id
var b64EncodedClientIDClientSecret = utils.GetBase64EncodedCredentials(credential.ClientId, credential.ClientSecret)
// set headers to request
headers := make(map[string]string)
headers[utils.HeaderContentType] = utils.HeaderValueXWWWFormUrlEncoded
headers[utils.HeaderAuthorization] = utils.HeaderValueAuthBasicPrefix + " " + b64EncodedClientIDClientSecret

//Create body for the request
body := utils.HeaderToken + token + utils.TokenTypeForRevocation

utils.Logln(utils.LogPrefixInfo + "connecting to " + tokenRevokeEndpoint)
resp, err := utils.InvokePOSTRequest(tokenRevokeEndpoint, headers, body)

if err != nil {
return err
}

//Check status code
if resp.StatusCode() != http.StatusOK {
return errors.New("Request didn't respond 200 OK for searching token revocation " +
"Status: " + resp.Status())
}
responseDataMap := make(map[string]string) // a map to hold response data
data := []byte(resp.Body())
json.Unmarshal(data, &responseDataMap) // add response data to the map
return nil
}
responseDataMap := make(map[string]string) // a map to hold response data
data := []byte(resp.Body())
json.Unmarshal(data, &responseDataMap) // add response data to the map
return nil
}
Loading

0 comments on commit 48aa1f5

Please sign in to comment.