Skip to content

Commit ffbde50

Browse files
authored
Merge pull request #258 from 1Password/andi_t/add_credential_name_id
Add IDs to credentials based on their name
2 parents 52df6ca + 26f3b30 commit ffbde50

6 files changed

Lines changed: 172 additions & 0 deletions

File tree

plugins/registry_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"testing"
55

66
"github.com/1Password/shell-plugins/sdk/schema"
7+
"github.com/stretchr/testify/assert"
78
)
89

910
func TestValidatePlugins(t *testing.T) {
@@ -17,3 +18,12 @@ func TestValidatePlugins(t *testing.T) {
1718
}
1819
}
1920
}
21+
22+
func TestAllPluginsHaveUniqueNames(t *testing.T) {
23+
var pluginNames []string
24+
for _, p := range registry {
25+
pluginNames = append(pluginNames, p.Name)
26+
}
27+
28+
assert.True(t, schema.IsStringSliceASet(pluginNames))
29+
}

sdk/names.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package sdk
22

3+
import (
4+
"regexp"
5+
"strings"
6+
)
7+
38
// FieldName represents a name of credential field. It should be title-cased.
49
// Examples: "Password", "Token", "API Key".
510
type FieldName string
@@ -15,3 +20,30 @@ type CredentialName string
1520
func (n CredentialName) String() string {
1621
return string(n)
1722
}
23+
24+
func (n CredentialName) ID() CredentialTypeID {
25+
return CredentialTypeID(credentialNameToSnakeCase(n))
26+
}
27+
28+
func credentialNameToSnakeCase(name CredentialName) string {
29+
const underscore = "_"
30+
31+
str := name.String()
32+
33+
charsToReplace := regexp.MustCompile(`[-/,. ]`)
34+
str = charsToReplace.ReplaceAllLiteralString(str, underscore)
35+
36+
multipleUnderscores := regexp.MustCompile(`_+`)
37+
str = multipleUnderscores.ReplaceAllString(str, underscore)
38+
39+
str = strings.TrimPrefix(str, "_")
40+
str = strings.TrimSuffix(str, "_")
41+
42+
return strings.ToLower(str)
43+
}
44+
45+
type CredentialTypeID string
46+
47+
func (i CredentialTypeID) String() string {
48+
return string(i)
49+
}

sdk/schema/credname/names_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package credname
2+
3+
import (
4+
"testing"
5+
6+
"github.com/1Password/shell-plugins/sdk"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestGettingCredentialIDsFromNames(t *testing.T) {
11+
names := []sdk.CredentialName{
12+
APIClientCredentials,
13+
APIKey,
14+
APIToken,
15+
AccessKey,
16+
AccessToken,
17+
AppPassword,
18+
AppToken,
19+
AuthToken,
20+
CLIToken,
21+
Credential,
22+
Credentials,
23+
DatabaseCredentials,
24+
LoginDetails,
25+
PersonalAPIToken,
26+
PersonalAccessToken,
27+
RegistryCredentials,
28+
SecretKey,
29+
sdk.CredentialName(""),
30+
sdk.CredentialName("Database-specific Credentials"),
31+
sdk.CredentialName("Public/Private Key-Pair"),
32+
sdk.CredentialName("Public/Private Key Pair"),
33+
sdk.CredentialName("KeyPair"),
34+
sdk.CredentialName("___some-test/name "),
35+
sdk.CredentialName("this -is/NOT-a, real. _- / life---- scENARIo..."),
36+
sdk.CredentialName("___some,.other test/name-which -is/NOT-a, real. , - ./ - life_scENARIo..."),
37+
}
38+
39+
expectedIDs := []string{
40+
"api_client_credentials",
41+
"api_key",
42+
"api_token",
43+
"access_key",
44+
"access_token",
45+
"app_password",
46+
"app_token",
47+
"auth_token",
48+
"cli_token",
49+
"credential",
50+
"credentials",
51+
"database_credentials",
52+
"login_details",
53+
"personal_api_token",
54+
"personal_access_token",
55+
"registry_credentials",
56+
"secret_key",
57+
"",
58+
"database_specific_credentials",
59+
"public_private_key_pair",
60+
"public_private_key_pair",
61+
"keypair",
62+
"some_test_name",
63+
"this_is_not_a_real_life_scenario",
64+
"some_other_test_name_which_is_not_a_real_life_scenario",
65+
}
66+
67+
for i, name := range names {
68+
assert.Equal(t, expectedIDs[i], name.ID().String())
69+
}
70+
}

sdk/schema/plugin.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ func (p Plugin) Validate() (bool, ValidationReport) {
8484
Severity: ValidationSeverityError,
8585
})
8686

87+
report.AddCheck(ValidationCheck{
88+
Description: "Credentials are uniquely identifiable inside a plugin",
89+
Assertion: NoDuplicateCredentials(p),
90+
Severity: ValidationSeverityError,
91+
})
92+
8793
return report.IsValid(), report
8894
}
8995

sdk/schema/validation.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,27 @@ func CredentialReferencesInCredentialList(plugin Plugin) bool {
112112
}
113113
return true
114114
}
115+
116+
func NoDuplicateCredentials(plugin Plugin) bool {
117+
var ids []string
118+
for _, credential := range plugin.Credentials {
119+
ids = append(ids, credential.Name.ID().String())
120+
}
121+
122+
return IsStringSliceASet(ids)
123+
}
124+
125+
func IsStringSliceASet(slice []string) bool {
126+
for i, s := range slice {
127+
if i == len(slice)-1 {
128+
break
129+
}
130+
for _, ss := range slice[i+1:] {
131+
if ss == s {
132+
return false
133+
}
134+
}
135+
}
136+
137+
return true
138+
}

sdk/schema/validation_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,33 @@ func TestPluginValidateEachReportFieldHasError(t *testing.T) {
135135

136136
assert.False(t, c.Assertion, fmt.Sprintf("\"%s\" validation is erroneous", c.Description))
137137
}
138+
139+
func TestIsStringSliceASet(t *testing.T) {
140+
testCases := []struct {
141+
slice []string
142+
assertion bool
143+
}{
144+
{
145+
slice: []string{"a", "b", "c", "b", "d"},
146+
assertion: false,
147+
}, {
148+
slice: []string{"a", "a", "a", "a", "a"},
149+
assertion: false,
150+
}, {
151+
slice: []string{"a", "a", "c", "d", "e"},
152+
assertion: false,
153+
}, {
154+
slice: []string{"a", "b", "c", "d", "d"},
155+
assertion: false,
156+
}, {
157+
slice: []string{"a", "b", "c", "d", "e"},
158+
assertion: true,
159+
}, {
160+
slice: []string{"a", "b", "b", "b", "d"},
161+
assertion: false,
162+
},
163+
}
164+
for _, tc := range testCases {
165+
assert.Equal(t, tc.assertion, IsStringSliceASet(tc.slice))
166+
}
167+
}

0 commit comments

Comments
 (0)