Skip to content

Commit

Permalink
merged main into feature branch and adapted to new way of cli
Browse files Browse the repository at this point in the history
  • Loading branch information
zeljkobekcic committed Aug 23, 2023
2 parents dcac2a5 + b7123d9 commit 8f07ae3
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 80 deletions.
36 changes: 33 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
- "*"
jobs:
goreleaser:
runs-on: ubuntu-22.04
runs-on: macos-13
steps:
- name: Checkout
uses: actions/checkout@v2
Expand All @@ -17,15 +17,45 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: 1.20
go-version: 1.19

- name: Import Code-Signing Certificates
uses: apple-actions/import-codesign-certs@v1
with:
p12-file-base64: ${{ secrets.MAC_CERT }}
p12-password: ${{ secrets.MAC_CERT_PASS }}
keychain: build
keychain-password: ${{ secrets.MAC_CERT_PASS }}

- name: Setup Keychain
run: |
KEYCHAIN=build.keychain
security default-keychain -s $KEYCHAIN
security unlock-keychain -p $MAC_CERT_PASS $KEYCHAIN
curl -o AppleWWDRCAG3.cer https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer
security import AppleWWDRCAG3.cer -k $KEYCHAIN -T /usr/bin/codesign
curl -o AppleRootCA.cer https://www.apple.com/appleca/AppleIncRootCertificate.cer
security import AppleRootCA.cer -k $KEYCHAIN -T /usr/bin/codesign
curl -o AppleDevIntermediate.cer https://www.apple.com/certificateauthority/DeveloperIDG2CA.cer
security import AppleDevIntermediate.cer -k $KEYCHAIN -T /usr/bin/codesign
security find-identity -v $KEYCHAIN
rm *.cer
env:
MAC_CERT_PASS: ${{ secrets.MAC_CERT_PASS }}

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3
with:
version: latest
args: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

NOTARYTOOL_PASS: ${{ secrets.NOTARYTOOL_PASS }}

- name: Read post build hook logs
if: always()
run: cat post_build_output.txt

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

Expand Down
5 changes: 5 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
builds:
- id: otc-auth
hooks:
post:
- cmd: ./signOSX.sh {{ .Path }} {{ .Os }} {{ .Version }} {{ .Arch }}
105 changes: 82 additions & 23 deletions accesstoken/accesstoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,34 +20,66 @@ func CreateAccessToken(tokenDescription string) {
log.Println("Creating access token file with GTC...")
resp, err := getAccessTokenFromServiceProvider(tokenDescription)
if err != nil {
// Handle error currently thrown when logged in by OIDC
var convErr golangsdk.ErrDefault404
if errors.As(err, &convErr) {
if convErr.ErrUnexpectedResponseCode.Actual == 404 &&
strings.Contains(convErr.ErrUnexpectedResponseCode.URL,
"OS-CREDENTIAL/credentials") {
common.OutputErrorMessageToConsoleAndExit(
"fatal: cannot generate AK/SK if logged in via OIDC")
}
// A 404 error is thrown when trying to create a permanent AK/SK when logged in with OIDC or SAML
var notFound golangsdk.ErrDefault404
if errors.As(err, &notFound) &&
strings.Contains(notFound.URL, "OS-CREDENTIAL/credentials") &&
strings.Contains(string(notFound.Body), "Could not find user:") {
common.OutputErrorMessageToConsoleAndExit(
"fatal: cannot create permanent access token when logged in via OIDC or SAML.")
} else {
common.OutputErrorToConsoleAndExit(err)
}
common.OutputErrorToConsoleAndExit(err)
}
makeAccessFile(resp, nil)
}

accessKeyFileContent := fmt.Sprintf(
"export OS_ACCESS_KEY=%s\n"+
"export AWS_ACCESS_KEY_ID=%s\n"+
"export OS_SECRET_KEY=%s\n"+
"export AWS_SECRET_ACCESS_KEY=%s",
resp.AccessKey,
resp.AccessKey,
resp.SecretKey,
resp.SecretKey)
func makeAccessFile(resp *credentials.Credential, tempResp *credentials.TemporaryCredential) {
if resp == nil && tempResp == nil {
common.OutputErrorMessageToConsoleAndExit("fatal: no temporary or permanent access keys to write")
}
var accessKeyFileContent string
if resp != nil {
accessKeyFileContent = fmt.Sprintf(
"export OS_ACCESS_KEY=%s\n"+
"export AWS_ACCESS_KEY_ID=%s\n"+
"export OS_SECRET_KEY=%s\n"+
"export AWS_SECRET_ACCESS_KEY=%s",
resp.AccessKey,
resp.AccessKey,
resp.SecretKey,
resp.SecretKey)
} else {
accessKeyFileContent = fmt.Sprintf(
"export OS_ACCESS_KEY=%s\n"+
"export AWS_ACCESS_KEY_ID=%s\n"+
"export OS_SECRET_KEY=%s\n"+
"export AWS_SECRET_ACCESS_KEY=%s\n"+
"export OS_SESSION_TOKEN=%s\n"+
"export AWS_SESSION_TOKEN=%s",
tempResp.AccessKey,
tempResp.AccessKey,
tempResp.SecretKey,
tempResp.SecretKey,
tempResp.SecurityToken,
tempResp.SecurityToken)
}

common.WriteStringToFile("./ak-sk-env.sh", accessKeyFileContent)
log.Println("Access token file created successfully.")
log.Println("Please source the ak-sk-env.sh file in the current directory manually")
}

func CreateTemporaryAccessToken(durationSeconds int) {
log.Println("Creating temporary access token file with GTC...")
resp, err := getTempAccessTokenFromServiceProvider(durationSeconds)
if err != nil {
common.OutputErrorToConsoleAndExit(err)
}

makeAccessFile(nil, resp)
}

func ListAccessToken() ([]credentials.Credential, error) {
client, err := getIdentityServiceClient()
if err != nil {
Expand All @@ -60,6 +92,22 @@ func ListAccessToken() ([]credentials.Credential, error) {
return credentials.List(client, credentials.ListOpts{UserID: user.ID}).Extract()
}

func getTempAccessTokenFromServiceProvider(durationSeconds int) (*credentials.TemporaryCredential, error) {
client, err := getIdentityServiceClient()
if err != nil {
return nil, err
}
tempCreds, err := credentials.CreateTemporary(client, credentials.CreateTemporaryOpts{
Methods: []string{"token"},
Duration: durationSeconds,
}).Extract()
if err != nil {
return nil, err
}
log.Printf("warning: access key will only be valid until: %v (UTC)", tempCreds.ExpiresAt)
return tempCreds, err
}

func getAccessTokenFromServiceProvider(tokenDescription string) (*credentials.Credential, error) {
client, err := getIdentityServiceClient()
if err != nil {
Expand All @@ -69,11 +117,22 @@ func getAccessTokenFromServiceProvider(tokenDescription string) (*credentials.Cr
if err != nil {
return nil, fmt.Errorf("couldn't get user: %w", err)
}
credential, err := credentials.Create(client, credentials.CreateOpts{
credResp := credentials.Create(client, credentials.CreateOpts{
UserID: user.ID,
Description: tokenDescription,
}).Extract()
})
credential, err := credResp.Extract()
if err != nil {
credential, err = handlePotentialLimitError(err, user, client, tokenDescription)
}
return credential, err
}

func handlePotentialLimitError(err error,
user *tokens.User,
client *golangsdk.ServiceClient,
tokenDescription string,
) (*credentials.Credential, error) {
var badRequest golangsdk.ErrDefault400
if errors.As(err, &badRequest) {
accessTokens, listErr := ListAccessToken()
Expand All @@ -88,10 +147,10 @@ func getAccessTokenFromServiceProvider(tokenDescription string) (*credentials.Cr
}
return nil, err
}
return credential, err
return nil, err
}

// Replaces AK/SKs made by otc-auth if their descriptions match the default..
// Replaces AK/SKs made by otc-auth if their descriptions match the default.
func conditionallyReplaceAccessTokens(user *tokens.User, client *golangsdk.ServiceClient,
tokenDescription string, accessTokens []credentials.Credential,
) (*credentials.Credential, error) {
Expand Down
150 changes: 96 additions & 54 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,27 @@ var cceGetKubeConfigCmd = &cobra.Command{
},
}

var tempAccessTokenCmd = &cobra.Command{
Use: "temp-access-token",
Short: accessTokenCmdHelp,
PersistentPreRunE: configureCmdFlagsAgainstEnvs(accessTokenFlagToEnv),
}

var tempAccessTokenCreateCmd = &cobra.Command{
Use: "create",
Short: tempAccessTokenCreateCmdHelp,
Example: tempAccessTokenCreateCmdExample,
Run: func(cmd *cobra.Command, args []string) {
config.LoadCloudConfig(domainName)
if !config.IsAuthenticationValid() {
common.OutputErrorMessageToConsoleAndExit(
"fatal: no valid unscoped token found.\n\nPlease obtain an unscoped token by logging in first.")
}

accesstoken.CreateTemporaryAccessToken(temporaryAccessTokenDurationSeconds)
},
}

var accessTokenCmd = &cobra.Command{
Use: "access-token",
Short: accessTokenCmdHelp,
Expand Down Expand Up @@ -374,6 +395,19 @@ func setupRootCmd() {
)
cceGetKubeConfigCmd.MarkFlagRequired(targetLocationFlag)

RootCmd.AddCommand(tempAccessTokenCmd)
tempAccessTokenCmd.PersistentFlags().StringVarP(&domainName, domainNameFlag, domainNameShortFlag, "", domainNameUsage)
tempAccessTokenCmd.MarkPersistentFlagRequired(domainNameFlag)

tempAccessTokenCmd.AddCommand(tempAccessTokenCreateCmd)
tempAccessTokenCmd.Flags().IntVarP(
&temporaryAccessTokenDurationSeconds,
temporaryAccessTokenDurationSecondsFlag,
temporaryAccessTokenDurationSecondsShortFlag,
15*60, // default is 15 minutes

Check failure on line 407 in cmd/root.go

View workflow job for this annotation

GitHub Actions / Lint the otc-auth source code

mnd: Magic number: 15, in <argument> detected (gomnd)

Check failure on line 407 in cmd/root.go

View workflow job for this annotation

GitHub Actions / Lint the otc-auth source code

mnd: Magic number: 15, in <argument> detected (gomnd)
temporaryAccessTokenDurationSecondsUsage,
)

RootCmd.AddCommand(accessTokenCmd)
accessTokenCmd.PersistentFlags().StringVarP(&domainName, domainNameFlag, domainNameShortFlag, "", domainNameUsage)
accessTokenCmd.MarkPersistentFlagRequired(domainNameFlag)
Expand Down Expand Up @@ -412,22 +446,23 @@ func setupRootCmd() {
}

var (
username string
password string
domainName string
overwriteToken bool
idpName string
idpURL string
totp string
userDomainID string
region string
projectName string
clusterName string
daysValid int
targetLocation string
accessTokenCreateDescription string
token string
openStackConfigLocation string
username string
password string
domainName string
overwriteToken bool
idpName string
idpURL string
totp string
userDomainID string
region string
projectName string
clusterName string
daysValid int
targetLocation string
accessTokenCreateDescription string
temporaryAccessTokenDurationSeconds int
token string
openStackConfigLocation string

loginIamFlagToEnv = map[string]string{
usernameFlag: usernameEnv,
Expand Down Expand Up @@ -560,6 +595,9 @@ $ export AK_SK_TOKEN=YourToken
$ otc-auth access-token delete
$ otc-auth access-token delete --token YourToken --os-domain-name YourDomain`
tempAccessTokenCreateCmdExample = `$ otc-auth temp-access-token create -t 900 -d YourDomainName # this creates a temp AK/SK which is 15 minutes valid (15 * 60 = 900)

Check failure on line 598 in cmd/root.go

View workflow job for this annotation

GitHub Actions / Lint the otc-auth source code

G101: Potential hardcoded credentials (gosec)

Check failure on line 598 in cmd/root.go

View workflow job for this annotation

GitHub Actions / Lint the otc-auth source code

G101: Potential hardcoded credentials (gosec)
$ otc-auth temp-access-token create --duration-seconds 1800`
openstackCmdHelp = "Manage Openstack Integration"
openstackConfigCreateCmdHelp = "Creates new clouds.yaml"
usernameFlag = "os-username"
Expand All @@ -577,44 +615,48 @@ $ otc-auth access-token delete --token YourToken --os-domain-name YourDomain`
overwriteTokenFlag = "overwrite-token"
overwriteTokenShortFlag = "o"
//nolint:gosec // This is not a hardcoded credential but a help message with a filename inside
overwriteTokenUsage = "Overrides .otc-info file."
idpNameFlag = "idp-name"
idpNameShortFlag = "i"
idpNameEnv = "IDP_NAME"
idpNameUsage = "Required for authentication with IdP."
idpURLFlag = "idp-url"
idpURLEnv = "IDP_URL"
idpURLUsage = "Required for authentication with IdP."
totpFlag = "totp"
totpShortFlag = "t"
totpUsage = "6-digit time-based one-time password (TOTP) used for the MFA login flow. Required together with the user-domain-id."
userDomainIDFlag = "os-user-domain-id"
userDomainIDEnv = "OS_USER_DOMAIN_ID"
userDomainIDUsage = "User Id number, can be obtained on the \"My Credentials page\" on the OTC. Required if --totp is provided. Either provide this argument or set the environment variable " + userDomainIDEnv + "."
regionFlag = "region"
regionShortFlag = "r"
regionEnv = "REGION"
regionUsage = "OTC region code. Either provide this argument or set the environment variable " + regionEnv + "."
projectNameFlag = "os-project-name"
projectNameShortFlag = "p"
projectNameEnv = "OS_PROJECT_NAME"
projectNameUsage = "Name of the project you want to access. Either provide this argument or set the environment variable " + projectNameEnv + "."
clusterNameFlag = "cluster"
clusterNameShortFlag = "c"
clusterNameEnv = "CLUSTER_NAME"
clusterNameUsage = "Name of the clusterArg you want to access. Either provide this argument or set the environment variable " + clusterNameEnv + "."
daysValidFlag = "days-valid"
daysValidDefaultValue = 7
daysValidShortFlag = "v"
daysValidUsage = "Period (in days) that the config will be valid."
targetLocationFlag = "target-location"
targetLocationShortFlag = "l"
targetLocationUsage = "Where the kube config should be saved"
accessTokenDescriptionFlag = "description"
accessTokenDescriptionShortFlag = "s"
accessTokenDescriptionUsage = "Description of the token"
accessTokenTokenFlag = "token"
accessTokenTokenShortFlag = "t"
overwriteTokenUsage = "Overrides .otc-info file."
idpNameFlag = "idp-name"
idpNameShortFlag = "i"
idpNameEnv = "IDP_NAME"
idpNameUsage = "Required for authentication with IdP."
idpURLFlag = "idp-url"
idpURLEnv = "IDP_URL"
idpURLUsage = "Required for authentication with IdP."
totpFlag = "totp"
totpShortFlag = "t"
totpUsage = "6-digit time-based one-time password (TOTP) used for the MFA login flow. Required together with the user-domain-id."
userDomainIDFlag = "os-user-domain-id"
userDomainIDEnv = "OS_USER_DOMAIN_ID"
userDomainIDUsage = "User Id number, can be obtained on the \"My Credentials page\" on the OTC. Required if --totp is provided. Either provide this argument or set the environment variable " + userDomainIDEnv + "."
regionFlag = "region"
regionShortFlag = "r"
regionEnv = "REGION"
regionUsage = "OTC region code. Either provide this argument or set the environment variable " + regionEnv + "."
projectNameFlag = "os-project-name"
projectNameShortFlag = "p"
projectNameEnv = "OS_PROJECT_NAME"
projectNameUsage = "Name of the project you want to access. Either provide this argument or set the environment variable " + projectNameEnv + "."
clusterNameFlag = "cluster"
clusterNameShortFlag = "c"
clusterNameEnv = "CLUSTER_NAME"
clusterNameUsage = "Name of the clusterArg you want to access. Either provide this argument or set the environment variable " + clusterNameEnv + "."
daysValidFlag = "days-valid"
daysValidDefaultValue = 7
daysValidShortFlag = "v"
daysValidUsage = "Period (in days) that the config will be valid."
targetLocationFlag = "target-location"
targetLocationShortFlag = "l"
targetLocationUsage = "Where the kube config should be saved"
accessTokenDescriptionFlag = "description"
accessTokenDescriptionShortFlag = "s"
accessTokenDescriptionUsage = "Description of the token"
accessTokenTokenFlag = "token"
accessTokenTokenShortFlag = "t"
tempAccessTokenCreateCmdHelp = "Manage temporary AK/SK"
temporaryAccessTokenDurationSecondsFlag = "duration-seconds"
temporaryAccessTokenDurationSecondsShortFlag = "t"
temporaryAccessTokenDurationSecondsUsage = "The token's lifetime, in seconds. Valid times are between 900 and 86400 seconds."
//nolint:gosec // This is not a hardcoded credential but a help message containing ak/sk
accessTokenTokenUsage = "The AK/SK token to delete."
openstackConfigCreateConfigLocationFlag = "config-location"
Expand Down
Loading

0 comments on commit 8f07ae3

Please sign in to comment.