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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
dist/

.env
baton-retool
80 changes: 37 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,38 @@
# baton-retool
`baton-retool` is a connector for [Retool](https://retool.com/) built using the [Baton SDK](https://github.com/conductorone/baton-sdk). It connects directly to your primary Retool Postgres database and syncs data about users, groups, organizations, pages, and resources.
`baton-retool` is a connector for [Retool](https://retool.com/) built using the [Baton SDK](https://github.com/conductorone/baton-sdk). It connects to the Retool API v2 and syncs data about users, groups, organizations, apps (pages), and resources.

Check out [Baton](https://github.com/conductorone/baton) to learn more the project in general.

# Getting Started

## Setup
1. While connected to the Retool database, create a new user for the connector to connect to Postgres as. Be sure to create and save the secure password for this user:
```postgresql
CREATE USER baton WITH PASSWORD 'secure-password';
```
2. Grant your new role the privileges required by the connector for inspecting retool privileges.
```postgresql
GRANT SELECT ("id", "name", "organizationId", "universalAccess", "universalResourceAccess", "universalQueryLibraryAccess",
"userListAccess", "auditLogAccess", "unpublishedReleaseAccess") ON groups TO baton;
GRANT SELECT, INSERT, UPDATE ("id", "accessLevel"), DELETE ON group_pages TO baton;
GRANT SELECT, INSERT, UPDATE ("id", "accessLevel") ON group_folder_defaults TO baton;
GRANT SELECT, INSERT, UPDATE ("id", "accessLevel") on group_resources TO baton;
GRANT SELECT, INSERT, UPDATE ("id", "accessLevel") on group_resource_folder_defaults TO baton;
GRANT SELECT ("id", "name") ON organizations TO baton;
GRANT SELECT ("id", "name", "organizationId", "folderId", "photoUrl", "description", "deletedAt") ON pages TO baton;
GRANT SELECT ("id", "name", "organizationId", "type", "displayName", "environmentId", "resourceFolderId") ON resources TO baton;
GRANT SELECT ("id", "email", "firstName", "lastName", "profilePhotoUrl", "userName", "enabled", "lastLoggedIn", "organizationId") ON users TO baton;
GRANT SELECT, INSERT, UPDATE, DELETE ("id", "userId", "groupId", "isAdmin", "updatedAt") ON user_groups TO baton;
GRANT USAGE, SELECT ON SEQUENCE user_groups_id_seq TO baton;
GRANT DELETE ON user_groups TO baton;
```

3. Run the connector with the proper connection string. For example if you created a new `baton` user with the password `baton`, it may look like this:
1. Generate a Retool API access token:
- Go to **Settings > API** in your Retool instance
- Create a new API token with the following scopes:
- **Users > Read** (to list and get users)
- **Users > Write** (for account provisioning - creating users)
- **Groups > Read** (to list groups and members)
- **Groups > Write** (for group membership management)
- **Permissions > Read** (to read app and resource permissions)
- **Permissions > Write** (for granting/revoking permissions)

2. Run the connector with your API token and Retool instance URL:
```bash
BATON_CONNECTION_STRING="user=baton password=baton host=localhost port=5432 dbname=hammerhead_production" baton-retool
BATON_API_TOKEN="your-retool-api-token" BATON_API_URL="https://your-org.retool.com" baton-retool
```

## brew

```
brew install conductorone/baton/baton conductorone/baton/baton-retool
baton-retool
BATON_API_TOKEN="your-retool-api-token" BATON_API_URL="https://your-org.retool.com" baton-retool
baton resources
```

## docker

```
docker run --rm -v $(pwd):/out -e BATON_CONNECTION_STRING="user=baton password=baton host=localhost port=5432 dbname=hammerhead_production" ghcr.io/conductorone/baton-retool:latest -f "/out/sync.c1z"
docker run --rm -v $(pwd):/out -e BATON_API_TOKEN="your-retool-api-token" -e BATON_API_URL="https://your-org.retool.com" ghcr.io/conductorone/baton-retool:latest -f "/out/sync.c1z"
docker run --rm -v $(pwd):/out ghcr.io/conductorone/baton:latest -f "/out/sync.c1z" resources
```

Expand All @@ -53,7 +42,7 @@ docker run --rm -v $(pwd):/out ghcr.io/conductorone/baton:latest -f "/out/sync.c
go install github.com/conductorone/baton/cmd/baton@main
go install github.com/conductorone/baton-retool/cmd/baton-retool@main

BATON_CONNECTION_STRING="user=baton password=baton host=localhost port=5432 dbname=hammerhead_production" baton-retool
BATON_API_TOKEN="your-retool-api-token" BATON_API_URL="https://your-org.retool.com" baton-retool
baton resources
```

Expand All @@ -63,9 +52,13 @@ baton resources
- Users
- Groups
- Organizations
- Pages
- Apps (Pages)
- Resources

## Account Provisioning

The connector supports creating new user accounts in Retool via the API. When provisioning is enabled (`-p` flag), the connector can create new users with their email, first name, and last name.

# Contributing, Support, and Issues

We started Baton because we were tired of taking screenshots and manually building spreadsheets. We welcome contributions, and ideas, no matter how small -- our goal is to make identity and permissions sprawl less painful for everyone. If you have questions, problems, or ideas: Please open a Github Issue!
Expand All @@ -87,20 +80,21 @@ Available Commands:
help Help about any command

Flags:
--client-id string The client ID used to authenticate with ConductorOne ($BATON_CLIENT_ID)
--client-secret string The client secret used to authenticate with ConductorOne ($BATON_CLIENT_SECRET)
--connection-string string required: The connection string for connecting to retool database ($BATON_CONNECTION_STRING)
-f, --file string The path to the c1z file to sync with ($BATON_FILE) (default "sync.c1z")
-h, --help help for baton-retool
--log-format string The output format for logs: json, console ($BATON_LOG_FORMAT) (default "json")
--log-level string The log level: debug, info, warn, error ($BATON_LOG_LEVEL) (default "info")
-p, --provisioning This must be set in order for provisioning actions to be enabled ($BATON_PROVISIONING)
--skip-disabled-users Skip syncing disabled users ($BATON_SKIP_DISABLED_USERS)
--skip-full-sync This must be set to skip a full sync ($BATON_SKIP_FULL_SYNC)
--skip-pages Skip syncing pages ($BATON_SKIP_PAGES)
--skip-resources Skip syncing resources ($BATON_SKIP_RESOURCES)
--ticketing This must be set to enable ticketing support ($BATON_TICKETING)
-v, --version version for baton-retool
--api-token string required: The Retool API access token ($BATON_API_TOKEN)
--api-url string required: The base URL of the Retool instance ($BATON_API_URL)
--client-id string The client ID used to authenticate with ConductorOne ($BATON_CLIENT_ID)
--client-secret string The client secret used to authenticate with ConductorOne ($BATON_CLIENT_SECRET)
-f, --file string The path to the c1z file to sync with ($BATON_FILE) (default "sync.c1z")
-h, --help help for baton-retool
--log-format string The output format for logs: json, console ($BATON_LOG_FORMAT) (default "json")
--log-level string The log level: debug, info, warn, error ($BATON_LOG_LEVEL) (default "info")
-p, --provisioning This must be set in order for provisioning actions to be enabled ($BATON_PROVISIONING)
--skip-disabled-users Skip syncing disabled/inactive users ($BATON_SKIP_DISABLED_USERS)
--skip-full-sync This must be set to skip a full sync ($BATON_SKIP_FULL_SYNC)
--skip-pages Skip syncing apps/pages ($BATON_SKIP_PAGES)
--skip-resources Skip syncing resources ($BATON_SKIP_RESOURCES)
--ticketing This must be set to enable ticketing support ($BATON_TICKETING)
-v, --version version for baton-retool

Use "baton-retool [command] --help" for more information about a command.
```
```
18 changes: 12 additions & 6 deletions cmd/baton-retool/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,33 @@ import (
)

var (
ConnectionString = field.StringField(
"connection-string",
APIToken = field.StringField(
"api-token",
field.WithRequired(true),
field.WithDescription("The connection string for connecting to retool database"),
field.WithDescription("The Retool API access token (Bearer token)"),
)
APIURL = field.StringField(
"api-url",
field.WithRequired(true),
field.WithDescription("The base URL of the Retool instance (e.g., https://myorg.retool.com)"),
)
SkipPages = field.BoolField(
"skip-pages",
field.WithDescription("Skip syncing pages"),
field.WithDescription("Skip syncing apps/pages"),
)
SkipResources = field.BoolField(
"skip-resources",
field.WithDescription("Skip syncing resources"),
)
SkipDisabledUsers = field.BoolField(
"skip-disabled-users",
field.WithDescription("Skip syncing disabled users"),
field.WithDescription("Skip syncing disabled/inactive users"),
)
)

var configurationFields = []field.SchemaField{
ConnectionString,
APIToken,
APIURL,
SkipPages,
SkipResources,
SkipDisabledUsers,
Expand Down
5 changes: 3 additions & 2 deletions cmd/baton-retool/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ func main() {
func getConnector(ctx context.Context, v *viper.Viper) (types.ConnectorServer, error) {
l := ctxzap.Extract(ctx)

connString := v.GetString(ConnectionString.FieldName)
apiToken := v.GetString(APIToken.FieldName)
apiURL := v.GetString(APIURL.FieldName)
skipPages := v.GetBool(SkipPages.FieldName)
skipResources := v.GetBool(SkipResources.FieldName)
skipDisabledUsers := v.GetBool(SkipDisabledUsers.FieldName)

cb, err := connector.New(ctx, connString, skipPages, skipResources, skipDisabledUsers)
cb, err := connector.New(ctx, apiURL, apiToken, skipPages, skipResources, skipDisabledUsers)
if err != nil {
l.Error("error creating connector builder", zap.Error(err))
return nil, err
Expand Down
12 changes: 2 additions & 10 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ toolchain go1.24.7
require (
github.com/conductorone/baton-sdk v0.2.66
github.com/ennyjfrick/ruleguard-logfatal v0.0.2
github.com/georgysavva/scany v1.2.1
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/jackc/pgx/v4 v4.18.1
github.com/quasilyte/go-ruleguard/dsl v0.3.23
github.com/spf13/viper v1.19.0
go.uber.org/zap v1.27.0
Expand Down Expand Up @@ -53,16 +51,9 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.14.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
github.com/jackc/pgtype v1.14.2 // indirect
github.com/jackc/puddle v1.3.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/lib/pq v1.10.2 // indirect
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
Expand All @@ -83,6 +74,7 @@ require (
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
Expand Down
Loading
Loading