Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,38 @@ intercube onboarding

The wizard can help you:
- configure login defaults (`username`, `password`, `scope`, `auth_method`, `instance_url`)
- optionally set sync defaults (`remote_user`, `file_syncing.from_server`, `file_syncing.path`)
- verify local prerequisites such as Boundary CLI and the Intercube sync helper
- optionally configure file path mappings for `intercube sync`
- verify local prerequisites such as Boundary CLI and `rsync`

After onboarding, use:

```bash
intercube login
intercube ssh
```

If required settings are missing (or the config file does not exist), the CLI
will prompt only when needed and save values automatically:
- `intercube login` prompts for required login settings
- `intercube sync files ...` prompts for missing sync defaults
- `intercube map` prompts to create mappings when none exist
- `intercube ssh` prompts for required login settings
- `intercube sync` prompts for missing file path mappings
- `intercube map --interactive` prompts to create mappings when none exist

`intercube login` is kept as a deprecated alias and prints a warning to use `intercube ssh`.

### Sync

Use sync from a source environment host:

```bash
intercube sync
intercube sync staging.example.com
intercube sync --files
intercube sync --database
intercube sync --dry-run
```

Behavior:
- always fetches current site inventory at runtime
- interactive target selection when no argument is passed
- argument auto-resolves against site ID/domain/server/user when possible
- stores only file path mappings in config (`sync.files.items`)
- database details are requested interactively for each run (not persisted)
82 changes: 82 additions & 0 deletions cmd/auth-login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package cmd

import (
"context"
"errors"
"fmt"
"strings"
"time"

"github.com/intercube/cli/util/appconfig"
authutil "github.com/intercube/cli/util/auth"
"github.com/spf13/cobra"
)

var authLoginCmd = &cobra.Command{
Use: "login",
Short: "Sign in with Clerk in your browser",
RunE: func(cmd *cobra.Command, args []string) error {
if err := appconfig.ValidateClerk(); err != nil {
return fmt.Errorf("%w (set via env/.env or build-time)", err)
}

store, err := authutil.NewSessionStore("intercube-cli")
if err != nil {
return err
}

var previousSession *authutil.Session
storedSession, loadErr := store.Load(cmd.Context())
if loadErr == nil {
previousSession = storedSession
} else if !errors.Is(loadErr, authutil.ErrNoSession) {
return loadErr
}

clerkClient := &authutil.ClerkClient{
Issuer: appconfig.ClerkIssuer,
ClientID: appconfig.ClerkClientID,
Audience: appconfig.ClerkAudience,
Scopes: appconfig.ClerkScopes,
CallbackPort: appconfig.ParsedCallbackPort(),
}

ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Minute)
defer cancel()

fmt.Println("Opening browser for Clerk sign-in...")
session, err := clerkClient.Login(ctx)
if err != nil {
return err
}

if previousSession != nil {
session.OrganizationID = strings.TrimSpace(previousSession.OrganizationID)
for _, known := range previousSession.KnownOrgIDs {
session.KnownOrgIDs = addKnownOrganizationID(session.KnownOrgIDs, known)
}
}

if appconfig.OrganizationID != "" {
session.KnownOrgIDs = addKnownOrganizationID(session.KnownOrgIDs, appconfig.OrganizationID)
}

session.KnownOrgIDs = addKnownOrganizationID(session.KnownOrgIDs, session.OrganizationID)

if err := store.Save(ctx, session); err != nil {
return err
}

fmt.Printf("Authenticated. Session expires at %s\n", session.ExpiresAt.Format(time.RFC3339))

if selectErr := runAuthOrgSelect(cmd, nil, true); selectErr != nil {
return selectErr
}

return nil
},
}

func init() {
authCmd.AddCommand(authLoginCmd)
}
42 changes: 42 additions & 0 deletions cmd/auth-logout.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package cmd

import (
"fmt"

"github.com/intercube/cli/util/appconfig"
authutil "github.com/intercube/cli/util/auth"
"github.com/spf13/cobra"
)

var authLogoutCmd = &cobra.Command{
Use: "logout",
Short: "Clear local API auth session",
RunE: func(cmd *cobra.Command, args []string) error {
store, err := authutil.NewSessionStore("intercube-cli")
if err != nil {
return err
}

session, err := store.Load(cmd.Context())
if err == nil && session != nil {
if appconfig.ValidateClerk() == nil {
clerkClient := &authutil.ClerkClient{
Issuer: appconfig.ClerkIssuer,
ClientID: appconfig.ClerkClientID,
}
_ = clerkClient.RevokeRefreshToken(cmd.Context(), session.RefreshToken)
}
}

if err := store.Clear(cmd.Context()); err != nil {
return err
}

fmt.Println("Signed out.")
return nil
},
}

func init() {
authCmd.AddCommand(authLogoutCmd)
}
Loading