* backend and clients * adding teams_team_name to workspace_settings * openapi.yaml adding teams workspace settings endpoints * teams workspace settings frontend * workspaces router * build ce * ee gate * ee oauth * update client * workspace error handler * remove log * ce * point to new hub scripts * updating hubPaths * updating hubPaths * cleanup * merge * schedule teams error * polish, reactivity * sqlx compilewarning * sqlx migrate * make it build * router * fix * remove sqlx workaround * Update ee-repo-ref.txt * simplify some logic * sqlx * latest ee ref * latest ee ref --------- Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
261 lines
8.8 KiB
Svelte
261 lines
8.8 KiB
Svelte
<script lang="ts">
|
|
import { enterpriseLicense } from '$lib/stores'
|
|
import { Alert, Button, Tab, Tabs } from './common'
|
|
|
|
import OAuthSetting from '$lib/components/OAuthSetting.svelte'
|
|
import OktaSetting from './OktaSetting.svelte'
|
|
import Auth0Setting from './Auth0Setting.svelte'
|
|
import CloseButton from './common/CloseButton.svelte'
|
|
import KeycloakSetting from './KeycloakSetting.svelte'
|
|
import CustomSso from './CustomSso.svelte'
|
|
import AuthentikSetting from '$lib/components/AuthentikSetting.svelte'
|
|
import AutheliaSetting from '$lib/components/AutheliaSetting.svelte'
|
|
import KanidmSetting from '$lib/components/KanidmSetting.svelte'
|
|
import ZitadelSetting from '$lib/components/ZitadelSetting.svelte'
|
|
import CustomOauth from './CustomOauth.svelte'
|
|
import { capitalize } from '$lib/utils'
|
|
import Toggle from './Toggle.svelte'
|
|
import { ExternalLink, Plus } from 'lucide-svelte'
|
|
|
|
export let snowflakeAccountIdentifier = ''
|
|
export let oauths: Record<string, any> = {}
|
|
export let requirePreexistingUserForOauth: boolean = false
|
|
|
|
const windmillBuiltins = [
|
|
'github',
|
|
'gitlab',
|
|
'bitbucket',
|
|
'slack',
|
|
'gsheets',
|
|
'gdrive',
|
|
'gmail',
|
|
'gcal',
|
|
'gforms',
|
|
'gcloud',
|
|
'gworkspace',
|
|
'basecamp',
|
|
'linkedin',
|
|
'quickbooks',
|
|
'visma',
|
|
'spotify',
|
|
'snowflake_oauth',
|
|
'teams',
|
|
'xero'
|
|
]
|
|
|
|
let oauth_name = undefined
|
|
|
|
let clientName = ''
|
|
let resourceName = ''
|
|
|
|
let tab: 'sso' | 'oauth' | 'scim' = 'sso'
|
|
</script>
|
|
|
|
<div>
|
|
<Tabs bind:selected={tab} class="mt-2 mb-4">
|
|
<Tab value="sso">SSO</Tab>
|
|
<Tab value="oauth">OAuth</Tab>
|
|
<Tab value="scim">SCIM/SAML</Tab>
|
|
</Tabs>
|
|
</div>
|
|
|
|
<div class="mb-6">
|
|
{#if tab === 'sso'}
|
|
{#if !$enterpriseLicense || $enterpriseLicense.endsWith('_pro')}
|
|
<Alert type="warning" title="Limited to 10 SSO users">
|
|
Without EE, the number of SSO users is limited to 10. SCIM/SAML is available on EE
|
|
</Alert>
|
|
{/if}
|
|
|
|
<div class="py-1" />
|
|
<div class="mb-2">
|
|
<span class="text-primary text-sm"
|
|
>When at least one of the below options is set, users will be able to login to Windmill via
|
|
their third-party account.
|
|
<br /> To test SSO, the recommended workflow is to to save the settings and try to login in
|
|
an incognito window.
|
|
<a target="_blank" href="https://www.windmill.dev/docs/misc/setup_oauth#sso">Learn more</a
|
|
></span
|
|
>
|
|
</div>
|
|
<div class="flex flex-col gap-3 py-4">
|
|
<OAuthSetting name="google" bind:value={oauths['google']} />
|
|
<OAuthSetting name="microsoft" bind:value={oauths['microsoft']} />
|
|
<OktaSetting bind:value={oauths['okta']} />
|
|
<Auth0Setting bind:value={oauths['auth0']} />
|
|
<OAuthSetting name="github" bind:value={oauths['github']} />
|
|
<OAuthSetting name="gitlab" bind:value={oauths['gitlab']} />
|
|
<OAuthSetting name="jumpcloud" bind:value={oauths['jumpcloud']} />
|
|
<KeycloakSetting bind:value={oauths['keycloak']} />
|
|
<AuthentikSetting bind:value={oauths['authentik']} />
|
|
<AutheliaSetting bind:value={oauths['authelia']} />
|
|
<KanidmSetting bind:value={oauths['kanidm']} />
|
|
<ZitadelSetting bind:value={oauths['zitadel']} />
|
|
{#each Object.keys(oauths) as k}
|
|
{#if !['authelia', 'authentik', 'google', 'microsoft', 'github', 'gitlab', 'jumpcloud', 'okta', 'auth0', 'keycloak', 'slack', 'kanidm', 'zitadel'].includes(k) && 'login_config' in oauths[k]}
|
|
{#if oauths[k]}
|
|
<div class="flex flex-col gap-2 pb-4">
|
|
<div class="flex flex-row items-center gap-2">
|
|
<!-- svelte-ignore a11y-label-has-associated-control -->
|
|
<label class="text-md font-medium text-primary">{k}</label>
|
|
<CloseButton
|
|
on:close={() => {
|
|
delete oauths[k]
|
|
oauths = { ...oauths }
|
|
}}
|
|
/>
|
|
</div>
|
|
<div class="p-2 border rounded">
|
|
<label class="block pb-2">
|
|
<span class="text-primary font-semibold text-sm">Custom Name</span>
|
|
<input
|
|
type="text"
|
|
placeholder="Custom Name"
|
|
bind:value={oauths[k]['display_name']}
|
|
/>
|
|
</label>
|
|
<label class="block pb-2">
|
|
<span class="text-primary font-semibold text-sm">Client Id</span>
|
|
<input type="text" placeholder="Client Id" bind:value={oauths[k]['id']} />
|
|
</label>
|
|
<label class="block pb-2">
|
|
<span class="text-primary font-semibold text-sm">Client Secret</span>
|
|
<input type="text" placeholder="Client Secret" bind:value={oauths[k]['secret']} />
|
|
</label>
|
|
{#if !windmillBuiltins.includes(k) && k != 'slack'}
|
|
<CustomSso bind:login_config={oauths[k]['login_config']} />
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
{/each}
|
|
</div>
|
|
<div class="flex gap-2 py-4">
|
|
<input type="text" placeholder="client_id" bind:value={clientName} />
|
|
<Button
|
|
variant="border"
|
|
color="blue"
|
|
hover="yo"
|
|
size="sm"
|
|
endIcon={{ icon: Plus }}
|
|
disabled={clientName == ''}
|
|
on:click={() => {
|
|
oauths[clientName] = { id: '', secret: '', login_config: {} }
|
|
clientName = ''
|
|
}}
|
|
>
|
|
Add custom SSO client {!$enterpriseLicense ? '(requires ee)' : ''}
|
|
</Button>
|
|
</div>
|
|
<div class="flex gap-2 py-4">
|
|
<Toggle
|
|
options={{
|
|
right: 'Require users to have been added manually to Windmill to sign in through OAuth'
|
|
}}
|
|
bind:checked={requirePreexistingUserForOauth}
|
|
/>
|
|
</div>
|
|
{:else if tab === 'oauth'}
|
|
<div class="mb-2">
|
|
<span class="text-primary text-sm"
|
|
>When one of the below options is set, you will be able to create a specific resource
|
|
containing a token automatically generated by the third-party provider.
|
|
<br />
|
|
To test it after setting an oauth client, go to the Resources menu and create a new one of the
|
|
type of your oauth client (i.e. a 'github' resource if you set Github OAuth).
|
|
<br /><a target="_blank" href="https://www.windmill.dev/docs/misc/setup_oauth#oauth"
|
|
>Learn more</a
|
|
></span
|
|
>
|
|
</div>
|
|
<div class="py-1" />
|
|
<OAuthSetting login={false} name="slack" bind:value={oauths['slack']} />
|
|
<div class="py-1" />
|
|
<OAuthSetting login={false} name="teams" eeOnly={true} bind:value={oauths['teams']} />
|
|
<div class="py-1" />
|
|
|
|
{#each Object.keys(oauths) as k}
|
|
{#if oauths[k] && !('login_config' in oauths[k])}
|
|
{#if !['slack', 'teams'].includes(k) && oauths[k]}
|
|
<div class="flex flex-col gap-2 pb-4">
|
|
<div class="flex flex-row items-center gap-2">
|
|
<!-- svelte-ignore a11y-label-has-associated-control -->
|
|
<label class="text-md font-medium text-primary">{k}</label>
|
|
<CloseButton
|
|
on:close={() => {
|
|
delete oauths[k]
|
|
oauths = { ...oauths }
|
|
}}
|
|
/>
|
|
</div>
|
|
<div class="p-2 border rounded">
|
|
<label class="block pb-2">
|
|
<span class="text-primary font-semibold text-sm">Client Id</span>
|
|
<input type="text" placeholder="Client Id" bind:value={oauths[k]['id']} />
|
|
</label>
|
|
<label class="block pb-2">
|
|
<span class="text-primary font-semibold text-sm">Client Secret</span>
|
|
<input type="text" placeholder="Client Secret" bind:value={oauths[k]['secret']} />
|
|
</label>
|
|
{#if !windmillBuiltins.includes(k) && k != 'slack'}
|
|
<CustomOauth bind:connect_config={oauths[k]['connect_config']} />
|
|
{/if}
|
|
{#if k == 'snowflake_oauth'}
|
|
<label class="block pb-2">
|
|
<span class="text-primary font-semibold text-sm flex gap-2 items-center"
|
|
><a
|
|
href="https://docs.snowflake.com/en/user-guide/admin-account-identifier#using-an-account-name-as-an-identifier"
|
|
target="_blank">Snowflake Account Identifier</a
|
|
><ExternalLink size={12} /></span
|
|
>
|
|
<input
|
|
type="text"
|
|
placeholder="<orgname>-<account_name>"
|
|
required={true}
|
|
bind:value={snowflakeAccountIdentifier}
|
|
/>
|
|
</label>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
{/each}
|
|
|
|
<div class="flex gap-2">
|
|
<select name="oauth_name" id="oauth_name" bind:value={oauth_name}>
|
|
<option value={undefined}>Select an OAuth client</option>
|
|
<option value="custom">Fully Custom (requires ee)</option>
|
|
{#each windmillBuiltins as name}
|
|
<option value={name}>{capitalize(name)}</option>
|
|
{/each}
|
|
</select>
|
|
{#if oauth_name == 'custom'}
|
|
<input type="text" placeholder="client_id" bind:value={resourceName} />
|
|
{:else}
|
|
<input type="text" value={oauth_name ?? ''} disabled />
|
|
{/if}
|
|
<Button
|
|
variant="border"
|
|
color="blue"
|
|
hover="yo"
|
|
size="sm"
|
|
endIcon={{ icon: Plus }}
|
|
disabled={!oauth_name ||
|
|
(oauth_name == 'custom' && resourceName == '') ||
|
|
(oauth_name == 'custom' && !$enterpriseLicense)}
|
|
on:click={() => {
|
|
let name = oauth_name == 'custom' ? resourceName : oauth_name
|
|
oauths[name ?? ''] = { id: '', secret: '' }
|
|
resourceName = ''
|
|
}}
|
|
>
|
|
Add OAuth client {oauth_name == 'custom' && !$enterpriseLicense ? '(requires ee)' : ''}
|
|
</Button>
|
|
</div>
|
|
{:else if tab == 'scim'}
|
|
<slot name="scim" />
|
|
{/if}
|
|
</div>
|