Skip to content

Commit f8e6de5

Browse files
committed
feat: add localxpose loclx cli
1 parent 61135fa commit f8e6de5

5 files changed

Lines changed: 207 additions & 0 deletions

File tree

plugins/localxpose/access_token.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package localxpose
2+
3+
import (
4+
"context"
5+
6+
"github.com/99designs/keyring"
7+
8+
"github.com/1Password/shell-plugins/sdk"
9+
"github.com/1Password/shell-plugins/sdk/importer"
10+
"github.com/1Password/shell-plugins/sdk/provision"
11+
"github.com/1Password/shell-plugins/sdk/schema"
12+
"github.com/1Password/shell-plugins/sdk/schema/credname"
13+
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
14+
)
15+
16+
const (
17+
serviceName = "https://loclx.io"
18+
keyringKeyName = "token"
19+
tokenFilePath = "~/.localxpose/.access"
20+
)
21+
22+
func AccessToken() schema.CredentialType {
23+
return schema.CredentialType{
24+
Name: credname.AccessToken,
25+
DocsURL: sdk.URL("https://localxpose.io/docs"),
26+
ManagementURL: sdk.URL("https://localxpose.io/dashboard/access"),
27+
Fields: []schema.CredentialField{
28+
{
29+
Name: fieldname.AccessToken,
30+
MarkdownDescription: "Token used to authenticate to LocalXpose.",
31+
Secret: true,
32+
Composition: &schema.ValueComposition{
33+
Length: 40,
34+
Charset: schema.Charset{
35+
Uppercase: true,
36+
Lowercase: true,
37+
Digits: true,
38+
},
39+
},
40+
},
41+
},
42+
DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping),
43+
Importer: importer.TryAll(
44+
importer.TryEnvVarPair(defaultEnvVarMapping),
45+
TryOSKeyring(),
46+
TryAccessTokenFile(),
47+
)}
48+
}
49+
50+
var defaultEnvVarMapping = map[string]sdk.FieldName{
51+
"LX_ACCESS_TOKEN": fieldname.AccessToken,
52+
}
53+
54+
func TryAccessTokenFile() sdk.Importer {
55+
return importer.TryFile(tokenFilePath, func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) {
56+
if contents.ToString() == "" {
57+
return
58+
}
59+
out.AddCandidate(sdk.ImportCandidate{
60+
Fields: map[sdk.FieldName]string{
61+
fieldname.AccessToken: contents.ToString(),
62+
},
63+
})
64+
})
65+
}
66+
67+
func TryOSKeyring() sdk.Importer {
68+
return func(ctx context.Context, in sdk.ImportInput, out *sdk.ImportOutput) {
69+
availableBackends := keyring.AvailableBackends()
70+
if len(availableBackends) == 0 {
71+
return
72+
}
73+
for _, backendType := range availableBackends {
74+
attempt := out.NewAttempt(importer.SourceOther(string(backendType), ""))
75+
openKeyring, err := keyring.Open(keyring.Config{
76+
KeychainTrustApplication: true,
77+
ServiceName: serviceName,
78+
})
79+
if err != nil {
80+
attempt.AddError(err)
81+
return
82+
}
83+
key, err := openKeyring.Get(keyringKeyName)
84+
if err != nil {
85+
attempt.AddError(err)
86+
continue
87+
}
88+
attempt.AddCandidate(sdk.ImportCandidate{
89+
Fields: map[sdk.FieldName]string{
90+
fieldname.AccessToken: string(key.Data),
91+
},
92+
})
93+
}
94+
}
95+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package localxpose
2+
3+
import (
4+
"testing"
5+
6+
"github.com/1Password/shell-plugins/sdk"
7+
"github.com/1Password/shell-plugins/sdk/plugintest"
8+
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
9+
)
10+
11+
func TestAccessTokenProvisioner(t *testing.T) {
12+
plugintest.TestProvisioner(t, AccessToken().DefaultProvisioner, map[string]plugintest.ProvisionCase{
13+
"default": {
14+
ItemFields: map[sdk.FieldName]string{
15+
fieldname.AccessToken: "PROVISIONERqLtcqQ8a3oRVfK5tiHzDOhEXAMPLE",
16+
},
17+
ExpectedOutput: sdk.ProvisionOutput{
18+
Environment: map[string]string{
19+
"LX_ACCESS_TOKEN": "PROVISIONERqLtcqQ8a3oRVfK5tiHzDOhEXAMPLE",
20+
},
21+
},
22+
},
23+
})
24+
}
25+
26+
func TestAccessTokenImporter(t *testing.T) {
27+
expectedFields := map[sdk.FieldName]string{
28+
fieldname.AccessToken: "31QJpgl8FB9qLtcqQ8a3oRVfK5tiHzDOhEXAMPLE",
29+
}
30+
31+
plugintest.TestImporter(t, AccessToken().Importer, map[string]plugintest.ImportCase{
32+
"environment": {
33+
Environment: map[string]string{
34+
"LX_ACCESS_TOKEN": "31QJpgl8FB9qLtcqQ8a3oRVfK5tiHzDOhEXAMPLE",
35+
},
36+
ExpectedCandidates: []sdk.ImportCandidate{
37+
{
38+
Fields: expectedFields,
39+
},
40+
},
41+
},
42+
"file": {
43+
Files: map[string]string{
44+
"~/.localxpose/.access": plugintest.LoadFixture(t, "localxpose.access"),
45+
},
46+
ExpectedCandidates: []sdk.ImportCandidate{
47+
{
48+
Fields: expectedFields,
49+
},
50+
},
51+
},
52+
})
53+
}

plugins/localxpose/localxpose.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package localxpose
2+
3+
import (
4+
"github.com/1Password/shell-plugins/sdk"
5+
"github.com/1Password/shell-plugins/sdk/needsauth"
6+
"github.com/1Password/shell-plugins/sdk/schema"
7+
"github.com/1Password/shell-plugins/sdk/schema/credname"
8+
)
9+
10+
func LocalXposeCLI() schema.Executable {
11+
return schema.Executable{
12+
Name: "LocalXpose CLI",
13+
Runs: []string{"loclx"},
14+
DocsURL: sdk.URL("https://localxpose.io/docs/cli"),
15+
NeedsAuth: needsauth.IfAll(
16+
needsauth.NotForHelpOrVersion(),
17+
needsauth.NotWhenContainsArgs("account", "login"), // skip 1Password authentication for "loclx account login" and its subcommands
18+
needsauth.NotWhenContainsArgs("a", "login"), // skip 1Password authentication for "loclx account login" and its subcommands
19+
needsauth.NotWhenContainsArgs("account", "logout"), // skip 1Password authentication for "loclx account logout" and its subcommands
20+
needsauth.NotWhenContainsArgs("a", "logout"), // skip 1Password authentication for "loclx account logout" and its subcommands
21+
needsauth.NotWhenContainsArgs("service", "restart"), // skip 1Password authentication for "loclx service restart" and its subcommands
22+
needsauth.NotWhenContainsArgs("service", "start"), // skip 1Password authentication for "loclx service start" and its subcommands
23+
needsauth.NotWhenContainsArgs("service", "status"), // skip 1Password authentication for "loclx service status" and its subcommands
24+
needsauth.NotWhenContainsArgs("service", "stop"), // skip 1Password authentication for "loclx service stop" and its subcommands
25+
needsauth.NotWhenContainsArgs("service", "uninstall"), // skip 1Password authentication for "loclx service uninstall" and its subcommands
26+
needsauth.NotWhenContainsArgs("setting"), // skip 1Password authentication for "loclx setting" and its subcommands
27+
needsauth.NotWhenContainsArgs("update"), // skip 1Password authentication for "loclx update" and its subcommands
28+
needsauth.NotWithoutArgs(),
29+
),
30+
Uses: []schema.CredentialUsage{
31+
{
32+
Name: credname.AccessToken,
33+
},
34+
},
35+
}
36+
}

plugins/localxpose/plugin.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package localxpose
2+
3+
import (
4+
"github.com/1Password/shell-plugins/sdk"
5+
"github.com/1Password/shell-plugins/sdk/schema"
6+
)
7+
8+
func New() schema.Plugin {
9+
return schema.Plugin{
10+
Name: "localxpose",
11+
Platform: schema.PlatformInfo{
12+
Name: "LocalXpose",
13+
Homepage: sdk.URL("https://localxpose.io"),
14+
},
15+
Credentials: []schema.CredentialType{
16+
AccessToken(),
17+
},
18+
Executables: []schema.Executable{
19+
LocalXposeCLI(),
20+
},
21+
}
22+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
31QJpgl8FB9qLtcqQ8a3oRVfK5tiHzDOhEXAMPLE

0 commit comments

Comments
 (0)