diff --git a/plugins/scaleway/api_key.go b/plugins/scaleway/api_key.go new file mode 100644 index 000000000..4ac6d54fe --- /dev/null +++ b/plugins/scaleway/api_key.go @@ -0,0 +1,130 @@ +package scaleway + +import ( + "context" + "os" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/importer" + "github.com/1Password/shell-plugins/sdk/provision" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func APIKey() schema.CredentialType { + return schema.CredentialType{ + Name: credname.APIKey, + DocsURL: sdk.URL("https://www.scaleway.com/en/docs/identity-and-access-management/iam/how-to/create-api-keys"), + ManagementURL: sdk.URL("https://console.scaleway.com/iam/api-keys"), + Fields: []schema.CredentialField{ + { + Name: fieldname.AccessKeyID, + MarkdownDescription: "The ID of the API Key used to authenticate to Scaleway.", + Secret: false, + Composition: &schema.ValueComposition{ + Length: 20, + Prefix: "SCW", + Charset: schema.Charset{ + Uppercase: true, + Digits: true, + }, + }, + }, + { + Name: fieldname.SecretAccessKey, + MarkdownDescription: "The secret access key used to authenticate to Scaleway.", + Secret: true, + Composition: &schema.ValueComposition{ + Length: 36, + Charset: schema.Charset{ + Lowercase: true, + Digits: true, + Specific: []rune{'-'}, + }, + }, + }, + { + Name: fieldname.DefaultOrganization, + MarkdownDescription: "The default organization ID to use for this access key.", + Secret: false, + Composition: &schema.ValueComposition{ + Length: 36, + Charset: schema.Charset{ + Lowercase: true, + Digits: true, + Specific: []rune{'-'}, + }, + }, + }, + { + Name: fieldname.DefaultRegion, + MarkdownDescription: "The default region to use for this access key.", + Optional: true, + }, + { + Name: fieldname.DefaultZone, + MarkdownDescription: "The default zone to use for this access key.", + Optional: true, + }, + }, + DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping), + Importer: importer.TryAll( + importer.TryEnvVarPair(defaultEnvVarMapping), + TryScalewayConfigFile(), + )} +} + +var defaultEnvVarMapping = map[string]sdk.FieldName{ + "SCW_ACCESS_KEY": fieldname.AccessKeyID, + "SCW_SECRET_KEY": fieldname.SecretAccessKey, + "SCW_DEFAULT_ORGANIZATION_ID": fieldname.DefaultOrganization, + "SCW_DEFAULT_REGION": fieldname.DefaultRegion, + "SCW_DEFAULT_ZONE": fieldname.DefaultZone, +} + +func TryScalewayConfigFile() sdk.Importer { + file := os.Getenv("SCW_CONFIG_PATH") + if file == "" { + file = "~/.config/scw/config.yaml" + } + return importer.TryFile(file, func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) { + var config Config + if err := contents.ToYAML(&config); err != nil { + out.AddError(err) + return + } + + for profileName, profile := range config.Profiles { + profileFields := make(map[sdk.FieldName]string) + profileFields[fieldname.AccessKeyID] = profile.AccessKey + profileFields[fieldname.SecretAccessKey] = profile.SecretKey + if profile.DefaultOrganizationID != "" { + profileFields[fieldname.DefaultOrganization] = profile.DefaultOrganizationID + } + if profile.DefaultRegion != "" { + profileFields[fieldname.DefaultRegion] = profile.DefaultRegion + } + if profile.DefaultZone != "" { + profileFields[fieldname.DefaultZone] = profile.DefaultZone + } + + out.AddCandidate(sdk.ImportCandidate{ + Fields: profileFields, + NameHint: importer.SanitizeNameHint(profileName), + }) + } + }) +} + +type Config struct { + Profiles map[string]Profile `yaml:"profiles"` +} + +type Profile struct { + AccessKey string `yaml:"access_key"` + SecretKey string `yaml:"secret_key"` + DefaultOrganizationID string `yaml:"default_organization_id"` + DefaultRegion string `yaml:"default_region"` + DefaultZone string `yaml:"default_zone"` +} diff --git a/plugins/scaleway/api_key_test.go b/plugins/scaleway/api_key_test.go new file mode 100644 index 000000000..616121eca --- /dev/null +++ b/plugins/scaleway/api_key_test.go @@ -0,0 +1,87 @@ +package scaleway + +import ( + "testing" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/plugintest" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func TestAPIKeyImporter(t *testing.T) { + plugintest.TestImporter(t, APIKey().Importer, map[string]plugintest.ImportCase{ + "Environment variables": { + Environment: map[string]string{ + "SCW_ACCESS_KEY": "SCWSYXTFI97NSEXAMPLE", + "SCW_SECRET_KEY": "d9b67b48-873c-8ece-8270-e1e15example", + "SCW_DEFAULT_ORGANIZATION_ID": "11111111-2222-3333-4444-55555example", + "SCW_DEFAULT_REGION": "fr-par", + "SCW_DEFAULT_ZONE": "fr-par-1", + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.AccessKeyID: "SCWSYXTFI97NSEXAMPLE", + fieldname.SecretAccessKey: "d9b67b48-873c-8ece-8270-e1e15example", + fieldname.DefaultOrganization: "11111111-2222-3333-4444-55555example", + fieldname.DefaultRegion: "fr-par", + fieldname.DefaultZone: "fr-par-1", + }, + }, + }, + }, + "SCW default config file location": { + Files: map[string]string{ + "~/.config/scw/config.yaml": plugintest.LoadFixture(t, "simple.yaml"), + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.AccessKeyID: "SCWSYXTFI97NSEXAMPLE", + fieldname.SecretAccessKey: "d9b67b48-873c-8ece-8270-e1e15example", + fieldname.DefaultOrganization: "11111111-2222-3333-4444-55555example", + }, + }, + }, + }, + "SCW config file with optional settings": { + Files: map[string]string{ + "~/.config/scw/config.yaml": plugintest.LoadFixture(t, "optional.yaml"), + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.AccessKeyID: "SCWSYXTFI97NSEXAMPLE", + fieldname.SecretAccessKey: "d9b67b48-873c-8ece-8270-e1e15example", + fieldname.DefaultOrganization: "11111111-2222-3333-4444-55555example", + fieldname.DefaultRegion: "fr-par", + fieldname.DefaultZone: "fr-par-1", + }, + }, + }, + }, + }) +} + +func TestAPIKeyProvisioner(t *testing.T) { + plugintest.TestProvisioner(t, APIKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{ + "default": { + ItemFields: map[sdk.FieldName]string{ + fieldname.AccessKeyID: "SCWSYXTFI97NSEXAMPLE", + fieldname.SecretAccessKey: "d9b67b48-873c-8ece-8270-e1e15example", + fieldname.DefaultOrganization: "11111111-2222-3333-4444-55555example", + fieldname.DefaultRegion: "fr-par", + fieldname.DefaultZone: "fr-par-1", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "SCW_ACCESS_KEY": "SCWSYXTFI97NSEXAMPLE", + "SCW_SECRET_KEY": "d9b67b48-873c-8ece-8270-e1e15example", + "SCW_DEFAULT_ORGANIZATION_ID": "11111111-2222-3333-4444-55555example", + "SCW_DEFAULT_REGION": "fr-par", + "SCW_DEFAULT_ZONE": "fr-par-1", + }, + }, + }, + }) +} diff --git a/plugins/scaleway/plugin.go b/plugins/scaleway/plugin.go new file mode 100644 index 000000000..23f3b4d25 --- /dev/null +++ b/plugins/scaleway/plugin.go @@ -0,0 +1,22 @@ +package scaleway + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema" +) + +func New() schema.Plugin { + return schema.Plugin{ + Name: "scaleway", + Platform: schema.PlatformInfo{ + Name: "Scaleway", + Homepage: sdk.URL("https://scaleway.com"), + }, + Credentials: []schema.CredentialType{ + APIKey(), + }, + Executables: []schema.Executable{ + ScalewayCLI(), + }, + } +} diff --git a/plugins/scaleway/scw.go b/plugins/scaleway/scw.go new file mode 100644 index 000000000..c94ae86aa --- /dev/null +++ b/plugins/scaleway/scw.go @@ -0,0 +1,27 @@ +package scaleway + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/needsauth" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" +) + +func ScalewayCLI() schema.Executable { + return schema.Executable{ + Name: "Scaleway CLI", + Runs: []string{"scw"}, + DocsURL: sdk.URL("https://www.scaleway.com/en/cli"), + NeedsAuth: needsauth.IfAll( + needsauth.NotForHelpOrVersion(), + needsauth.NotWithoutArgs(), + needsauth.NotWhenContainsArgs("-c"), + needsauth.NotWhenContainsArgs("--config"), + ), + Uses: []schema.CredentialUsage{ + { + Name: credname.APIKey, + }, + }, + } +} diff --git a/plugins/scaleway/test-fixtures/optional.yaml b/plugins/scaleway/test-fixtures/optional.yaml new file mode 100644 index 000000000..73b4e51c2 --- /dev/null +++ b/plugins/scaleway/test-fixtures/optional.yaml @@ -0,0 +1,6 @@ +--- +access_key: SCWSYXTFI97NSEXAMPLE +secret_key: d9b67b48-873c-8ece-8270-e1e15example +default_organization_id: 11111111-2222-3333-4444-55555example +default_region: fr-par +default_zone: fr-par-1 diff --git a/plugins/scaleway/test-fixtures/simple.yaml b/plugins/scaleway/test-fixtures/simple.yaml new file mode 100644 index 000000000..ce0b02001 --- /dev/null +++ b/plugins/scaleway/test-fixtures/simple.yaml @@ -0,0 +1,3 @@ +access_key: SCWSYXTFI97NSEXAMPLE +secret_key: d9b67b48-873c-8ece-8270-e1e15example +default_organization_id: 11111111-2222-3333-4444-55555example diff --git a/sdk/schema/fieldname/names.go b/sdk/schema/fieldname/names.go index d8cebacf3..39f4b50ad 100644 --- a/sdk/schema/fieldname/names.go +++ b/sdk/schema/fieldname/names.go @@ -4,56 +4,59 @@ import "github.com/1Password/shell-plugins/sdk" // Credential field names. const ( - APIHost = sdk.FieldName("API Host") - APIKey = sdk.FieldName("API Key") - APIKeyID = sdk.FieldName("API Key ID") - APISecret = sdk.FieldName("API Secret") - AccessKeyID = sdk.FieldName("Access Key ID") - AccessToken = sdk.FieldName("Access Token") - Account = sdk.FieldName("Account") - AccountID = sdk.FieldName("Account ID") - AccountSID = sdk.FieldName("Account SID") - Address = sdk.FieldName("Address") - AppKey = sdk.FieldName("App Key") - AppSecret = sdk.FieldName("App Secret") - AppToken = sdk.FieldName("App Token") - AuthToken = sdk.FieldName("Auth Token") - Authtoken = sdk.FieldName("Authtoken") - Cert = sdk.FieldName("Cert") - Certificate = sdk.FieldName("Certificate") - ClientSecret = sdk.FieldName("Client Secret") - ClientToken = sdk.FieldName("Client Token") - Credential = sdk.FieldName("Credential") - Credentials = sdk.FieldName("Credentials") - Database = sdk.FieldName("Database") - DefaultRegion = sdk.FieldName("Default Region") - Email = sdk.FieldName("Email") - Endpoint = sdk.FieldName("Endpoint") - Host = sdk.FieldName("Host") - HostAddress = sdk.FieldName("Host Address") - Key = sdk.FieldName("Key") - MFASerial = sdk.FieldName("MFA Serial") - Mode = sdk.FieldName("Mode") - Namespace = sdk.FieldName("Namespace") - OneTimePassword = sdk.FieldName("One-Time Password") - OrgID = sdk.FieldName("Org ID") - OrgURL = sdk.FieldName("Org URL") - Organization = sdk.FieldName("Organization") - Password = sdk.FieldName("Password") - Port = sdk.FieldName("Port") - PublicKey = sdk.FieldName("Public Key") - PrivateKey = sdk.FieldName("Private Key") - ProjectID = sdk.FieldName("Project ID") - Project = sdk.FieldName("Project") - Region = sdk.FieldName("Region") - Secret = sdk.FieldName("Secret") - SecretAccessKey = sdk.FieldName("Secret Access Key") - Subdomain = sdk.FieldName("Subdomain") - Token = sdk.FieldName("Token") - URL = sdk.FieldName("URL") - User = sdk.FieldName("User") - Username = sdk.FieldName("Username") - Website = sdk.FieldName("Website") + + APIHost = sdk.FieldName("API Host") + APIKey = sdk.FieldName("API Key") + APIKeyID = sdk.FieldName("API Key ID") + APISecret = sdk.FieldName("API Secret") + AccessKeyID = sdk.FieldName("Access Key ID") + AccessToken = sdk.FieldName("Access Token") + Account = sdk.FieldName("Account") + AccountID = sdk.FieldName("Account ID") + AccountSID = sdk.FieldName("Account SID") + Address = sdk.FieldName("Address") + AppKey = sdk.FieldName("App Key") + AppSecret = sdk.FieldName("App Secret") + AppToken = sdk.FieldName("App Token") + AuthToken = sdk.FieldName("Auth Token") + Authtoken = sdk.FieldName("Authtoken") + Cert = sdk.FieldName("Cert") + Certificate = sdk.FieldName("Certificate") + ClientSecret = sdk.FieldName("Client Secret") + ClientToken = sdk.FieldName("Client Token") + Credential = sdk.FieldName("Credential") + Credentials = sdk.FieldName("Credentials") + Database = sdk.FieldName("Database") + DefaultRegion = sdk.FieldName("Default Region") + DefaultOrganization = sdk.FieldName("Default Organization") + DefaultZone = sdk.FieldName("Default Zone") + Email = sdk.FieldName("Email") + Endpoint = sdk.FieldName("Endpoint") + Host = sdk.FieldName("Host") + HostAddress = sdk.FieldName("Host Address") + Key = sdk.FieldName("Key") + MFASerial = sdk.FieldName("MFA Serial") + Mode = sdk.FieldName("Mode") + Namespace = sdk.FieldName("Namespace") + OneTimePassword = sdk.FieldName("One-Time Password") + OrgURL = sdk.FieldName("Org URL") + Organization = sdk.FieldName("Organization") + Password = sdk.FieldName("Password") + Port = sdk.FieldName("Port") + PublicKey = sdk.FieldName("Public Key") + PrivateKey = sdk.FieldName("Private Key") + Region = sdk.FieldName("Region") + Secret = sdk.FieldName("Secret") + SecretAccessKey = sdk.FieldName("Secret Access Key") + Subdomain = sdk.FieldName("Subdomain") + Token = sdk.FieldName("Token") + URL = sdk.FieldName("URL") + User = sdk.FieldName("User") + Username = sdk.FieldName("Username") + Website = sdk.FieldName("Website") + + + ) func ListAll() []sdk.FieldName { @@ -81,6 +84,8 @@ func ListAll() []sdk.FieldName { Credentials, Database, DefaultRegion, + DefaultOrganization, + DefaultZone, Endpoint, Host, HostAddress, @@ -107,4 +112,5 @@ func ListAll() []sdk.FieldName { Username, Website, } + }