Skip to content

Commit

Permalink
Merge pull request #234 from wongle/akamai-plugin
Browse files Browse the repository at this point in the history
New plugin: Akamai CLI
  • Loading branch information
hculea committed Apr 18, 2023
2 parents 65ba5f7 + bfd12cb commit 00ab385
Show file tree
Hide file tree
Showing 8 changed files with 317 additions and 16 deletions.
33 changes: 33 additions & 0 deletions plugins/akamai/akamai.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package akamai

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 AkamaiCLI() schema.Executable {
return schema.Executable{
Name: "Akamai CLI",
Runs: []string{"akamai"},
DocsURL: sdk.URL("https://techdocs.akamai.com/developer/docs/about-clis"),
NeedsAuth: needsauth.IfAll(
needsauth.NotForHelpOrVersion(),
needsauth.NotWithoutArgs(),
needsauth.NotWhenContainsArgs("config"), // skip 1Password authentication for "akamai config" and its subcommands
needsauth.NotWhenContainsArgs("install"), // skip 1Password authentication for "akamai install" and its subcommands
needsauth.NotWhenContainsArgs("get"), // skip 1Password authentication for "akamai get" and its subcommands
needsauth.NotWhenContainsArgs("list"), // skip 1Password authentication for "akamai list" and its subcommands
needsauth.NotWhenContainsArgs("search"), // skip 1Password authentication for "akamai search" and its subcommands
needsauth.NotWhenContainsArgs("uninstall"), // skip 1Password authentication for "akamai uninstall" and its subcommands
needsauth.NotWhenContainsArgs("update"), // skip 1Password authentication for "akamai update" and its subcommands
needsauth.NotWhenContainsArgs("upgrade"), // skip 1Password authentication for "akamai upgrade" and its subcommands
),
Uses: []schema.CredentialUsage{
{
Name: credname.APIClientCredentials,
},
},
}
}
145 changes: 145 additions & 0 deletions plugins/akamai/api_client_credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package akamai

import (
"context"

"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 APIClientCredentials() schema.CredentialType {
return schema.CredentialType{
Name: credname.APIClientCredentials,
DocsURL: sdk.URL("https://techdocs.akamai.com/developer/docs/set-up-authentication-credentials"),
ManagementURL: sdk.URL("https://control.akamai.com/apps/identity-management/#/tabs/users/list"),
Fields: []schema.CredentialField{
{
Name: fieldname.ClientSecret,
MarkdownDescription: "Client Secret used to authenticate to Akamai.",
Secret: true,
Composition: &schema.ValueComposition{
Length: 44,
Charset: schema.Charset{
Uppercase: true,
Lowercase: true,
Digits: true,
Symbols: true,
},
},
},
{
Name: fieldname.Host,
MarkdownDescription: "Hostname used to authenticate to Akamai APIs.",
Secret: false,
Composition: &schema.ValueComposition{
Length: 58,
Charset: schema.Charset{
Lowercase: true,
Digits: true,
Specific: []rune{45, 46}, // "-" and "."
},
},
},
{
Name: fieldname.AccessToken,
MarkdownDescription: "Access Token used to authenticate to Akamai.",
Secret: false,
Composition: &schema.ValueComposition{
Length: 38,
Charset: schema.Charset{
Lowercase: true,
Digits: true,
Specific: []rune{45}, // "-"
},
},
},
{
Name: fieldname.ClientToken,
MarkdownDescription: "Client Token used to authenticate to Akamai.",
Secret: false,
Composition: &schema.ValueComposition{
Length: 38,
Charset: schema.Charset{
Lowercase: true,
Digits: true,
Specific: []rune{45}, // "-"
},
},
},
},
DefaultProvisioner: provision.TempFile(configFile,
provision.Filename(".edgerc"),
provision.AddArgs(
"--edgerc", "{{ .Path }}",
"--section", "default",
),
provision.SetPathAsEnvVar("EDGERC"), // for Akamai Terraform provider
),
Importer: importer.TryAll(
TryAkamaiConfigFile(),
)}
}

func configFile(in sdk.ProvisionInput) ([]byte, error) {
contents := "[default]\n"

if clientsecret, ok := in.ItemFields[fieldname.ClientSecret]; ok {
contents += "client_secret = " + clientsecret + "\n"
}

if host, ok := in.ItemFields[fieldname.Host]; ok {
contents += "host = " + host + "\n"
}

if accesstoken, ok := in.ItemFields[fieldname.AccessToken]; ok {
contents += "access_token = " + accesstoken + "\n"
}

if clienttoken, ok := in.ItemFields[fieldname.ClientToken]; ok {
contents += "client_token = " + clienttoken + "\n"
}

return []byte(contents), nil
}

// Load credentials from the ~/.edgerc file.
// Import separate credentials into 1Password based on the sections in the ~/.edgerc file.
func TryAkamaiConfigFile() sdk.Importer {
return importer.TryFile("~/.edgerc", func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) {
configFile, err := contents.ToINI()
if err != nil {
out.AddError(err)
return
}

for _, section := range configFile.Sections() {
profileName := section.Name()

fields := make(map[sdk.FieldName]string)
if section.HasKey("client_secret") && section.Key("client_secret").Value() != "" {
fields[fieldname.ClientSecret] = section.Key("client_secret").Value()
}
if section.HasKey("host") && section.Key("host").Value() != "" {
fields[fieldname.Host] = section.Key("host").Value()
}
if section.HasKey("access_token") && section.Key("access_token").Value() != "" {
fields[fieldname.AccessToken] = section.Key("access_token").Value()
}
if section.HasKey("client_token") && section.Key("client_token").Value() != "" {
fields[fieldname.ClientToken] = section.Key("client_token").Value()
}

// add candidates that contain all required credential fields
if fields[fieldname.ClientSecret] != "" && fields[fieldname.Host] != "" && fields[fieldname.AccessToken] != "" && fields[fieldname.ClientToken] != "" {
out.AddCandidate(sdk.ImportCandidate{
NameHint: importer.SanitizeNameHint(profileName),
Fields: fields,
})
}
}
})
}
77 changes: 77 additions & 0 deletions plugins/akamai/api_client_credentials_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package akamai

import (
"testing"

"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/plugintest"
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
)

func TestAPIClientCredentialsProvisioner(t *testing.T) {
plugintest.TestProvisioner(t, APIClientCredentials().DefaultProvisioner, map[string]plugintest.ProvisionCase{
"default": {
ItemFields: map[sdk.FieldName]string{
fieldname.ClientSecret: "abcdE23FNkBxy456z25qx9Yp5CPUxlEfQeTDkfh4QA=I",
fieldname.Host: "akab-lmn789n2k53w7qrs-nfkxaa4lfk3kd6ym.luna.akamaiapis.net",
fieldname.AccessToken: "akab-zyx987xa6osbli4k-e7jf5ikib5jknes3",
fieldname.ClientToken: "akab-nomoflavjuc4422e-fa2xznerxrm3teg7",
},
ExpectedOutput: sdk.ProvisionOutput{
CommandLine: []string{"--edgerc", "/tmp/.edgerc", "--section", "default"},
Files: map[string]sdk.OutputFile{
"/tmp/.edgerc": {Contents: []byte(plugintest.LoadFixture(t, ".edgerc-single"))},
},
Environment: map[string]string{
"EDGERC": "/tmp/.edgerc",
},
},
},
})
}

func TestAPIClientCredentialsImporter(t *testing.T) {
plugintest.TestImporter(t, APIClientCredentials().Importer, map[string]plugintest.ImportCase{
"config file with single credential": {
Files: map[string]string{
"~/.edgerc": plugintest.LoadFixture(t, ".edgerc-single"),
},
ExpectedCandidates: []sdk.ImportCandidate{
{
NameHint: "",
Fields: map[sdk.FieldName]string{
fieldname.ClientSecret: "abcdE23FNkBxy456z25qx9Yp5CPUxlEfQeTDkfh4QA=I",
fieldname.Host: "akab-lmn789n2k53w7qrs-nfkxaa4lfk3kd6ym.luna.akamaiapis.net",
fieldname.AccessToken: "akab-zyx987xa6osbli4k-e7jf5ikib5jknes3",
fieldname.ClientToken: "akab-nomoflavjuc4422e-fa2xznerxrm3teg7",
},
},
},
},
"config file with multiple credentials": {
Files: map[string]string{
"~/.edgerc": plugintest.LoadFixture(t, ".edgerc-multiple"),
},
ExpectedCandidates: []sdk.ImportCandidate{
{
NameHint: "",
Fields: map[sdk.FieldName]string{
fieldname.ClientSecret: "abcdE23FNkBxy456z25qx9Yp5CPUxlEfQeTDkfh4QA=I",
fieldname.Host: "akab-lmn789n2k53w7qrs-nfkxaa4lfk3kd6ym.luna.akamaiapis.net",
fieldname.AccessToken: "akab-zyx987xa6osbli4k-e7jf5ikib5jknes3",
fieldname.ClientToken: "akab-nomoflavjuc4422e-fa2xznerxrm3teg7",
},
},
{
NameHint: "newcredential",
Fields: map[sdk.FieldName]string{
fieldname.ClientSecret: "M9XGZP/D2JedcbABC4Td8XSnHfKKIV4N5n28cj2y6zE=",
fieldname.Host: "akab-ip5n2k53w7nhdcxy-nflxabc432DE1ymd.luna.akamaiapis.net",
fieldname.AccessToken: "akab-abc77fxa6zyxi4k-e7jf5ikib5jknesc3",
fieldname.ClientToken: "akab-moo22awk8765efd-s2yw5zqfrx4jp57cf",
},
},
},
},
})
}
22 changes: 22 additions & 0 deletions plugins/akamai/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package akamai

import (
"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/schema"
)

func New() schema.Plugin {
return schema.Plugin{
Name: "akamai",
Platform: schema.PlatformInfo{
Name: "Akamai",
Homepage: sdk.URL("https://akamai.com"),
},
Credentials: []schema.CredentialType{
APIClientCredentials(),
},
Executables: []schema.Executable{
AkamaiCLI(),
},
}
}
11 changes: 11 additions & 0 deletions plugins/akamai/test-fixtures/.edgerc-multiple
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[default]
client_secret = abcdE23FNkBxy456z25qx9Yp5CPUxlEfQeTDkfh4QA=I
host = akab-lmn789n2k53w7qrs-nfkxaa4lfk3kd6ym.luna.akamaiapis.net
access_token = akab-zyx987xa6osbli4k-e7jf5ikib5jknes3
client_token = akab-nomoflavjuc4422e-fa2xznerxrm3teg7

[newcredential]
client_secret = M9XGZP/D2JedcbABC4Td8XSnHfKKIV4N5n28cj2y6zE=
host = akab-ip5n2k53w7nhdcxy-nflxabc432DE1ymd.luna.akamaiapis.net
access_token = akab-abc77fxa6zyxi4k-e7jf5ikib5jknesc3
client_token = akab-moo22awk8765efd-s2yw5zqfrx4jp57cf
5 changes: 5 additions & 0 deletions plugins/akamai/test-fixtures/.edgerc-single
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[default]
client_secret = abcdE23FNkBxy456z25qx9Yp5CPUxlEfQeTDkfh4QA=I
host = akab-lmn789n2k53w7qrs-nfkxaa4lfk3kd6ym.luna.akamaiapis.net
access_token = akab-zyx987xa6osbli4k-e7jf5ikib5jknes3
client_token = akab-nomoflavjuc4422e-fa2xznerxrm3teg7
34 changes: 18 additions & 16 deletions sdk/schema/credname/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,28 @@ import "github.com/1Password/shell-plugins/sdk"

// Credential type names.
const (
APIKey = sdk.CredentialName("API Key")
APIToken = sdk.CredentialName("API Token")
AccessKey = sdk.CredentialName("Access Key")
AccessToken = sdk.CredentialName("Access Token")
AppPassword = sdk.CredentialName("App Password")
AppToken = sdk.CredentialName("App Token")
AuthToken = sdk.CredentialName("Auth Token")
CLIToken = sdk.CredentialName("CLI Token")
Credential = sdk.CredentialName("Credential")
Credentials = sdk.CredentialName("Credentials")
DatabaseCredentials = sdk.CredentialName("Database Credentials")
LoginDetails = sdk.CredentialName("Login Details")
PersonalAPIToken = sdk.CredentialName("Personal API Token")
PersonalAccessToken = sdk.CredentialName("Personal Access Token")
RegistryCredentials = sdk.CredentialName("Registry Credentials")
SecretKey = sdk.CredentialName("Secret Key")
APIClientCredentials = sdk.CredentialName("API Client Credentials")
APIKey = sdk.CredentialName("API Key")
APIToken = sdk.CredentialName("API Token")
AccessKey = sdk.CredentialName("Access Key")
AccessToken = sdk.CredentialName("Access Token")
AppPassword = sdk.CredentialName("App Password")
AppToken = sdk.CredentialName("App Token")
AuthToken = sdk.CredentialName("Auth Token")
CLIToken = sdk.CredentialName("CLI Token")
Credential = sdk.CredentialName("Credential")
Credentials = sdk.CredentialName("Credentials")
DatabaseCredentials = sdk.CredentialName("Database Credentials")
LoginDetails = sdk.CredentialName("Login Details")
PersonalAPIToken = sdk.CredentialName("Personal API Token")
PersonalAccessToken = sdk.CredentialName("Personal Access Token")
RegistryCredentials = sdk.CredentialName("Registry Credentials")
SecretKey = sdk.CredentialName("Secret Key")
)

func ListAll() []sdk.CredentialName {
return []sdk.CredentialName{
APIClientCredentials,
APIKey,
APIToken,
AccessKey,
Expand Down
6 changes: 6 additions & 0 deletions sdk/schema/fieldname/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const (
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")
Expand All @@ -20,6 +21,8 @@ const (
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")
Expand Down Expand Up @@ -56,6 +59,7 @@ func ListAll() []sdk.FieldName {
APIKeyID,
APISecret,
AccessKeyID,
AccessToken,
Account,
AccountID,
AccountSID,
Expand All @@ -67,6 +71,8 @@ func ListAll() []sdk.FieldName {
Authtoken,
Cert,
Certificate,
ClientSecret,
ClientToken,
Credential,
Credentials,
Database,
Expand Down

0 comments on commit 00ab385

Please sign in to comment.