1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-18 03:13:47 +00:00

2️⃣ Add frontend to set authentication providers of a user source

This commit is contained in:
Jérémie Pardou 2024-02-09 08:12:40 +00:00
parent 8ac97de75a
commit d39487e944
22 changed files with 785 additions and 303 deletions
enterprise/web-frontend/modules/baserow_enterprise
web-frontend/modules
builder
core
integrations/localBaserow/components/integrations

View file

@ -13,3 +13,4 @@
@import 'crud_table_filters';
@import 'long_text_field';
@import 'highest_role_field';
@import 'local_baserow_password_app_auth_provider_form';

View file

@ -0,0 +1,9 @@
.local-baserow-password-app-auth-provider-form__field-selector {
& .control__label--small {
font-weight: normal;
}
& .control__elements {
margin-top: 0;
}
}

View file

@ -0,0 +1,20 @@
import { AppAuthProviderType } from '@baserow/modules/core/appAuthProviderTypes'
import LocalBaserowUserSourceForm from '@baserow_enterprise/integrations/localBaserow/components/appAuthProviders/LocalBaserowPasswordAppAuthProviderForm'
export class LocalBaserowPasswordAppAuthProviderType extends AppAuthProviderType {
static getType() {
return 'local_baserow_password'
}
get name() {
return this.app.i18n.t('appAuthProviderType.localBaserowPassword')
}
get formComponent() {
return LocalBaserowUserSourceForm
}
getOrder() {
return 10
}
}

View file

@ -0,0 +1,87 @@
<template>
<form>
<FormGroup
:label="$t('localBaserowPasswordAppAuthProviderForm.passwordFieldLabel')"
small-label
horizontal-variable
class="local-baserow-password-app-auth-provider-form__field-selector"
>
<Dropdown
v-model="values.password_field_id"
fixed-items
:disabled="!selectedTable"
:placeholder="
$t('localBaserowPasswordAppAuthProviderForm.passwordFieldLabel')
"
>
<DropdownItem
v-for="field in selectedTable?.fields || []"
:key="field.id"
:name="field.name"
:value="field.id"
:icon="getIconForType(field.type)"
/>
<template #emptyState>
{{ $t('localBaserowPasswordAppAuthProviderForm.noFields') }}
</template>
</Dropdown>
</FormGroup>
</form>
</template>
<script>
import form from '@baserow/modules/core/mixins/form'
export default {
mixins: [form],
props: {
integration: {
type: Object,
required: true,
},
currentUserSource: {
type: Object,
required: true,
},
},
data() {
return {
values: {
password_field_id: null,
},
allowedValues: ['password_field_id'],
}
},
computed: {
databases() {
return this.integration.context_data.databases
},
fieldTypes() {
return this.$registry.getAll('field')
},
selectedTable() {
if (!this.currentUserSource.table_id) {
return null
}
for (const database of this.databases) {
for (const table of database.tables) {
if (table.id === this.currentUserSource.table_id) {
return table
}
}
}
return null
},
},
watch: {
'currentUserSource.table_id'() {
this.values.password_field_id = null
},
},
methods: {
getIconForType(type) {
return this.fieldTypes[type].getIconClass()
},
},
}
</script>

View file

@ -1,295 +1,301 @@
{
"clientHandler": {
"cannotDisableAllAuthProvidersTitle": "Last enabled provider",
"cannotDisableAllAuthProvidersDescription": "It is not possible to disable or delete last enabled provider."
"clientHandler": {
"cannotDisableAllAuthProvidersTitle": "Last enabled provider",
"cannotDisableAllAuthProvidersDescription": "It is not possible to disable or delete last enabled provider."
},
"enterprise": {
"license": "Enterprise",
"sidebarTooltip": "Your account has access to the enterprise features globally",
"rbac": "RBAC",
"sso": "SSO",
"deactivated": "Available in advanced/enterprise",
"licenseDescription": "Viewers are free with Baserow Enterprise. If a user has any other role, in any workspace then they will use a paid seat automatically.",
"overflowWarning": "You have too many non-viewer users and have used up all of your paid seats. Change users to become viewers on each workspaces members page."
},
"trashType": {
"team": "team"
},
"createTeamModal": {
"title": "Create team",
"invalidNameTitle": "Please use a different name",
"invalidNameMessage": "This team name is already in use in the specified workspace."
},
"updateTeamModal": {
"title": "Update {teamName}",
"invalidNameTitle": "Please use a different name",
"invalidNameMessage": "This team name is already in use in the specified workspace."
},
"manageTeamModals": {
"subheading": "Teams allow you to give or restrict permissions in bulk to multiple people."
},
"manageTeamForm": {
"submit": "Save",
"nameTitle": "Name",
"roleTitle": "Default role",
"inviteMembers": "Add members",
"membersTitle": "Members",
"noSubjectsSelected": "Click '{buttonLabel}' to optionally add team users.",
"roleHelpText": "User specific roles will always override Team roles."
},
"editTeamContext": {
"edit": "Edit team",
"remove": "Delete team"
},
"teamsTable": {
"title": "{teamCount} teams in {workspaceName}",
"createNew": "Create team",
"nameColumn": "Name",
"createdColumn": "Created on",
"subjectsColumn": "Members",
"roleColumn": "Default role",
"roleHelpText": "Can be overriden with roles on databases and tables."
},
"subjectSampleField": {
"titleAttr": "{subjectCount} members in this team."
},
"teamsSettings": {
"teamsTabTitle": "Teams"
},
"adminType": {
"AuditLog": "Audit log",
"Authentication": "Authentication"
},
"auditLog": {
"adminTitle": "Audit log",
"workspaceTitle": "Audit log - {workspaceName}",
"filterUserTitle": "User",
"filterWorkspaceTitle": "Workspace",
"filterActionTypeTitle": "Event Type",
"filterFromTimestampTitle": "From Date",
"filterFromTimestamp": "From Date",
"filterToTimestampTitle": "To Date",
"filterToTimestamp": "To Date",
"user": "User",
"workspace": "Workspace",
"actionType": "Event Type",
"description": "Description",
"timestamp": "Timestamp",
"ip_address": "IP Address",
"exportToCsv": "Export to CSV",
"exportModalTitle": "Export to CSV",
"clearFilters": "Clear",
"allUsers": "All Users",
"allWorkspaces": "All Workspaces",
"allActionTypes": "All Event Types"
},
"auditLogExportModal": {
"title": "Export to CSV",
"exportFilename": "Admin Audit Log Export - {date}",
"exportWorkspaceFilename": " Workspace ({workspaceId}) Audit Log Export - {date}",
"cancelledTitle": "Export failed",
"cancelledDescription": "Something went wrong while exporting the audit log. Please try again."
},
"authProviders": {
"title": "Authentication Providers",
"noProviders": "No authentication providers configured yet.",
"addProvider": "Add provider"
},
"authProviderTypes": {
"password": "Email and password authentication"
},
"editAuthProviderMenuContext": {
"edit": "Edit",
"delete": "Delete"
},
"samlSettingsForm": {
"domain": "Domain",
"domainPlaceholder": "Insert the company domain name...",
"invalidDomain": "Invalid domain name",
"domainAlreadyExists": "A SAML provider for this domain already exists",
"metadata": "Metadata",
"metadataPlaceholder": "Paste the Identity Provider metadata...",
"invalidMetadata": "Invalid XML metadata for the Identity Provider",
"relayStateUrl": "Default Relay State URL",
"acsUrl": "Single Sign On URL",
"providerIsVerified": "The provider has been verified"
},
"createSettingsAuthProviderModal": {
"title": "Add a new {type}"
},
"updateSettingsAuthProviderModal": {
"title": "Edit {name}"
},
"deleteAuthProviderModal": {
"title": "Delete {name}",
"comment": "Do you really want to delete this {type}?"
},
"loginWithSaml": {
"signInWithSaml": "Login with SAML SSO",
"redirecting": "Redirecting to the Identity Provider...",
"continueWithSaml": "Continue to the SAML SSO provider",
"requestError": "This domain is not configured with SAML SSO",
"loginText": "Already have a password?"
},
"loginError": {
"title": "Something went wrong:",
"loginText": "Already have an account?",
"help": "Please contact your administrator for help.",
"defaultErrorMessage": "it was not possible to complete the SSO login flow.",
"errorSsoFeatureNotActive": "SSO feature is not active. Please contact your administrator.",
"errorInvalidSamlRequest": "SAML request is invalid.",
"errorInvalidSamlResponse": "SAML response is invalid.",
"errorUserDeactivated": "user has been disabled.",
"errorProviderDoesNotExist": "this authentication provider doesn't exist or is not available.",
"errorAuthFlowError": "an error occurred during the authentication flow with the selected provider.",
"errorDifferentProvider": "please use the provider that you originally signed up with.",
"errorWorkspaceInvitationEmailMismatch": "your email address does not match with the invitation.",
"errorSignupDisabled": "sign up has been disabled by the administrator."
},
"roles": {
"admin": {
"name": "Admin",
"description": "Can fully configure and edit workspaces and applications."
},
"enterprise": {
"license": "Enterprise",
"sidebarTooltip": "Your account has access to the enterprise features globally",
"rbac": "RBAC",
"sso": "SSO",
"deactivated": "Available in advanced/enterprise",
"licenseDescription": "Viewers are free with Baserow Enterprise. If a user has any other role, in any workspace then they will use a paid seat automatically.",
"overflowWarning": "You have too many non-viewer users and have used up all of your paid seats. Change users to become viewers on each workspaces members page."
"builder": {
"name": "Builder",
"description": "Can fully configure and edit applications."
},
"trashType": {
"team": "team"
"editor": {
"name": "Editor",
"description": "Can fully edit data but not configure applications or workspaces."
},
"createTeamModal": {
"title": "Create team",
"invalidNameTitle": "Please use a different name",
"invalidNameMessage": "This team name is already in use in the specified workspace."
"commenter": {
"name": "Commenter",
"description": "Can only read and comment on data."
},
"updateTeamModal": {
"title": "Update {teamName}",
"invalidNameTitle": "Please use a different name",
"invalidNameMessage": "This team name is already in use in the specified workspace."
"viewer": {
"name": "Viewer",
"description": "Can only read data."
},
"manageTeamModals": {
"subheading": "Teams allow you to give or restrict permissions in bulk to multiple people."
"noAccess": {
"name": "No access",
"description": "Can't access anything."
},
"manageTeamForm": {
"submit": "Save",
"nameTitle": "Name",
"roleTitle": "Default role",
"inviteMembers": "Add members",
"membersTitle": "Members",
"noSubjectsSelected": "Click '{buttonLabel}' to optionally add team users.",
"roleHelpText": "User specific roles will always override Team roles."
},
"editTeamContext": {
"edit": "Edit team",
"remove": "Delete team"
},
"teamsTable": {
"title": "{teamCount} teams in {workspaceName}",
"createNew": "Create team",
"nameColumn": "Name",
"createdColumn": "Created on",
"subjectsColumn": "Members",
"roleColumn": "Default role",
"roleHelpText": "Can be overriden with roles on databases and tables."
},
"subjectSampleField": {
"titleAttr": "{subjectCount} members in this team."
},
"teamsSettings": {
"teamsTabTitle": "Teams"
},
"adminType": {
"AuditLog": "Audit log",
"Authentication": "Authentication"
},
"auditLog": {
"adminTitle": "Audit log",
"workspaceTitle": "Audit log - {workspaceName}",
"filterUserTitle": "User",
"filterWorkspaceTitle": "Workspace",
"filterActionTypeTitle": "Event Type",
"filterFromTimestampTitle": "From Date",
"filterFromTimestamp": "From Date",
"filterToTimestampTitle": "To Date",
"filterToTimestamp": "To Date",
"user": "User",
"workspace": "Workspace",
"actionType": "Event Type",
"description": "Description",
"timestamp": "Timestamp",
"ip_address": "IP Address",
"exportToCsv": "Export to CSV",
"exportModalTitle": "Export to CSV",
"clearFilters": "Clear",
"allUsers": "All Users",
"allWorkspaces": "All Workspaces",
"allActionTypes": "All Event Types"
},
"auditLogExportModal": {
"title": "Export to CSV",
"exportFilename": "Admin Audit Log Export - {date}",
"exportWorkspaceFilename": " Workspace ({workspaceId}) Audit Log Export - {date}",
"cancelledTitle": "Export failed",
"cancelledDescription": "Something went wrong while exporting the audit log. Please try again."
},
"authProviders": {
"title": "Authentication Providers",
"noProviders": "No authentication providers configured yet.",
"addProvider": "Add provider"
},
"authProviderTypes": {
"password": "Email and password authentication"
},
"editAuthProviderMenuContext": {
"edit": "Edit",
"delete": "Delete"
},
"samlSettingsForm": {
"domain": "Domain",
"domainPlaceholder": "Insert the company domain name...",
"invalidDomain": "Invalid domain name",
"domainAlreadyExists": "A SAML provider for this domain already exists",
"metadata": "Metadata",
"metadataPlaceholder": "Paste the Identity Provider metadata...",
"invalidMetadata": "Invalid XML metadata for the Identity Provider",
"relayStateUrl": "Default Relay State URL",
"acsUrl": "Single Sign On URL",
"providerIsVerified": "The provider has been verified"
},
"createSettingsAuthProviderModal": {
"title": "Add a new {type}"
},
"updateSettingsAuthProviderModal": {
"title": "Edit {name}"
},
"deleteAuthProviderModal": {
"title": "Delete {name}",
"comment": "Do you really want to delete this {type}?"
},
"loginWithSaml": {
"signInWithSaml": "Login with SAML SSO",
"redirecting": "Redirecting to the Identity Provider...",
"continueWithSaml": "Continue to the SAML SSO provider",
"requestError": "This domain is not configured with SAML SSO",
"loginText": "Already have a password?"
},
"loginError": {
"title": "Something went wrong:",
"loginText": "Already have an account?",
"help": "Please contact your administrator for help.",
"defaultErrorMessage": "it was not possible to complete the SSO login flow.",
"errorSsoFeatureNotActive": "SSO feature is not active. Please contact your administrator.",
"errorInvalidSamlRequest": "SAML request is invalid.",
"errorInvalidSamlResponse": "SAML response is invalid.",
"errorUserDeactivated": "user has been disabled.",
"errorProviderDoesNotExist": "this authentication provider doesn't exist or is not available.",
"errorAuthFlowError": "an error occurred during the authentication flow with the selected provider.",
"errorDifferentProvider": "please use the provider that you originally signed up with.",
"errorWorkspaceInvitationEmailMismatch": "your email address does not match with the invitation.",
"errorSignupDisabled": "sign up has been disabled by the administrator."
},
"roles": {
"admin": {
"name": "Admin",
"description": "Can fully configure and edit workspaces and applications."
},
"builder": {
"name": "Builder",
"description": "Can fully configure and edit applications."
},
"editor": {
"name": "Editor",
"description": "Can fully edit data but not configure applications or workspaces."
},
"commenter": {
"name": "Commenter",
"description": "Can only read and comment on data."
},
"viewer": {
"name": "Viewer",
"description": "Can only read data."
},
"noAccess": {
"name": "No access",
"description": "Can't access anything."
},
"noRoleLowPriority": {
"name": "No role",
"description": "Get default workspace role from their teams."
}
},
"memberRolesModal": {
"memberRolesDatabaseTabTitle": "Database",
"memberRolesTableTabTitle": "Current Table",
"error": {
"title": "Could not fetch data",
"description": "An error occurred while fetching the member data."
}
},
"memberRolesTab": {
"database": {
"title": "{name} database",
"selectMembers": "Select members",
"everyoneHasAccess": "Everyone at {name} workspace can access this database.",
"onlyYouHaveAccess": "Only you can access this database.",
"warningTitle": "Other users might inherit access!",
"warningMessage": "Other users might inherit access via their respective roles on the parent workspace.",
"headerTooltip": "Database roles will override workspace roles."
},
"table": {
"title": "{name} table",
"selectMembers": "Select members",
"everyoneHasAccess": "Everyone at {name} workspace can access this table.",
"onlyYouHaveAccess": "Only you can access this table.",
"warningTitle": "Other users might inherit access!",
"warningMessage": "Other users might inherit access via their respective roles on the parent database or workspace.",
"headerTooltip": "Table roles will override workspace and database roles."
}
},
"memberRolesShareToggle": {
"label": "Share with everyone at {name}",
"subLabel": "{totalUserAmount} workspace members"
},
"memberRolesDatabaseContexItem": {
"label": "Manage members"
},
"memberRolesTableContexItem": {
"label": "Manage members"
},
"memberRolesMembersList": {
"remove": "Remove",
"teamMembersCount": "{count} team members",
"adminHelpText": "Admins can restrict other admins roles.",
"noMembers": "No members have been selected. Use the select button to add one."
},
"roleAssignmentModal": {
"membersTab": "Members",
"teamsTab": "Teams",
"subjectTypeNameMembers": "members",
"subjectTypeNameTeams": "teams"
},
"selectMembersList": {
"searchPlaceholder": "Find members...",
"selectedAmountLabel": "{count} members selected"
},
"selectTeamsList": {
"searchPlaceholder": "Find teams...",
"selectedAmountLabel": "{count} teams selected"
},
"selectSubjectsListFooter": {
"invite": "Invite {count} {type}",
"types": {
"members": "members",
"teams": "teams"
},
"helpTooltip": "Member role assignments override team role assignments."
},
"membersRoleField": {
"noAccessHelpText": "This user requires explicit access to resources.",
"adminHelpText": "Admins can restrict other admins roles."
},
"membersPagePlugin": {
"roleHelpText": "Can be overriden with roles on databases and tables."
},
"oauthSettingsForm": {
"providerName": "Name",
"providerNamePlaceholder": "Custom provider name",
"clientId": "Client ID",
"clientIdPlaceholder": "Provider's client ID",
"secret": "Secret",
"secretPlaceholder": "Provider's secret",
"baseUrl": "URL",
"baseUrlPlaceholder": "Provider's base URL",
"invalidBaseUrl": "The entered URL is not a valid provider URL",
"callbackUrl": "Callback URL"
},
"loginButton": {
"continueWith": "Continue with"
},
"enterpriseFeatures": {
"sso": "Single Sign On",
"rbac": "Role-based access control"
},
"chatwootSupportSidebarWorkspace": {
"directSupport": "Direct support"
},
"userTeamsField": {
"no_records": "No teams"
},
"snapshotModalWarning": {
"message": "Please be aware that a snapshot will include any permissions set on the application and its tables."
},
"auditLogSidebarWorkspace": {
"title": "Audit log",
"deactivated": "Available in advanced/enterprise"
},
"localBaserowUserSourceType": {
"notConfigured": "Not configured"
},
"localBaserowUserSourceForm": {
"description": "Every row in the table is a user account. Please select the matching fields in the table. The email field will be used to identify.",
"emailFieldLabel": "Select email field",
"emailFieldLabelPlaceholder": "Select a field...",
"noFields": "No fields",
"nameFieldLabel": "Select name field",
"nameFieldPlaceholder": "Select a field..."
"noRoleLowPriority": {
"name": "No role",
"description": "Get default workspace role from their teams."
}
},
"memberRolesModal": {
"memberRolesDatabaseTabTitle": "Database",
"memberRolesTableTabTitle": "Current Table",
"error": {
"title": "Could not fetch data",
"description": "An error occurred while fetching the member data."
}
},
"memberRolesTab": {
"database": {
"title": "{name} database",
"selectMembers": "Select members",
"everyoneHasAccess": "Everyone at {name} workspace can access this database.",
"onlyYouHaveAccess": "Only you can access this database.",
"warningTitle": "Other users might inherit access!",
"warningMessage": "Other users might inherit access via their respective roles on the parent workspace.",
"headerTooltip": "Database roles will override workspace roles."
},
"table": {
"title": "{name} table",
"selectMembers": "Select members",
"everyoneHasAccess": "Everyone at {name} workspace can access this table.",
"onlyYouHaveAccess": "Only you can access this table.",
"warningTitle": "Other users might inherit access!",
"warningMessage": "Other users might inherit access via their respective roles on the parent database or workspace.",
"headerTooltip": "Table roles will override workspace and database roles."
}
},
"memberRolesShareToggle": {
"label": "Share with everyone at {name}",
"subLabel": "{totalUserAmount} workspace members"
},
"memberRolesDatabaseContexItem": {
"label": "Manage members"
},
"memberRolesTableContexItem": {
"label": "Manage members"
},
"memberRolesMembersList": {
"remove": "Remove",
"teamMembersCount": "{count} team members",
"adminHelpText": "Admins can restrict other admins roles.",
"noMembers": "No members have been selected. Use the select button to add one."
},
"roleAssignmentModal": {
"membersTab": "Members",
"teamsTab": "Teams",
"subjectTypeNameMembers": "members",
"subjectTypeNameTeams": "teams"
},
"selectMembersList": {
"searchPlaceholder": "Find members...",
"selectedAmountLabel": "{count} members selected"
},
"selectTeamsList": {
"searchPlaceholder": "Find teams...",
"selectedAmountLabel": "{count} teams selected"
},
"selectSubjectsListFooter": {
"invite": "Invite {count} {type}",
"types": {
"members": "members",
"teams": "teams"
},
"helpTooltip": "Member role assignments override team role assignments."
},
"membersRoleField": {
"noAccessHelpText": "This user requires explicit access to resources.",
"adminHelpText": "Admins can restrict other admins roles."
},
"membersPagePlugin": {
"roleHelpText": "Can be overriden with roles on databases and tables."
},
"oauthSettingsForm": {
"providerName": "Name",
"providerNamePlaceholder": "Custom provider name",
"clientId": "Client ID",
"clientIdPlaceholder": "Provider's client ID",
"secret": "Secret",
"secretPlaceholder": "Provider's secret",
"baseUrl": "URL",
"baseUrlPlaceholder": "Provider's base URL",
"invalidBaseUrl": "The entered URL is not a valid provider URL",
"callbackUrl": "Callback URL"
},
"loginButton": {
"continueWith": "Continue with"
},
"enterpriseFeatures": {
"sso": "Single Sign On",
"rbac": "Role-based access control"
},
"chatwootSupportSidebarWorkspace": {
"directSupport": "Direct support"
},
"userTeamsField": {
"no_records": "No teams"
},
"snapshotModalWarning": {
"message": "Please be aware that a snapshot will include any permissions set on the application and its tables."
},
"auditLogSidebarWorkspace": {
"title": "Audit log",
"deactivated": "Available in advanced/enterprise"
},
"localBaserowUserSourceType": {
"notConfigured": "Not configured"
},
"localBaserowUserSourceForm": {
"description": "Every row in the table is a user account. Please select the matching fields in the table. The email field will be used to identify.",
"emailFieldLabel": "Select email field",
"emailFieldLabelPlaceholder": "Select a field...",
"noFields": "No fields",
"nameFieldLabel": "Select name field",
"nameFieldPlaceholder": "Select a field..."
},
"appAuthProviderType": {
"localBaserowPassword": "Email/Password"
},
"localBaserowPasswordAppAuthProviderForm": {
"passwordFieldLabel": "Select password field"
}
}

View file

@ -26,6 +26,7 @@ import {
} from '@baserow_enterprise/licenseTypes'
import { EnterprisePlugin } from '@baserow_enterprise/plugins'
import { LocalBaserowUserSourceType } from '@baserow_enterprise/integrations/userSourceTypes'
import { LocalBaserowPasswordAppAuthProviderType } from '@baserow_enterprise/integrations/appAuthProviderTypes'
export default (context) => {
const { app, isDev, store } = context
@ -89,4 +90,9 @@ export default (context) => {
app.$registry.register('license', new EnterpriseLicenseType(context))
app.$registry.register('userSource', new LocalBaserowUserSourceType(context))
app.$registry.register(
'appAuthProvider',
new LocalBaserowPasswordAppAuthProviderType(context)
)
}

View file

@ -242,7 +242,6 @@ export default {
})
},
changeFieldType(fieldToUpdate, newType) {
console.log('newType', newType)
this.values.fields = this.values.fields.map((field) => {
if (field.id === fieldToUpdate.id) {
return { id: field.id, name: field.name, type: newType }

View file

@ -16,7 +16,7 @@
:image="getIntegrationType(integration).image"
:title="integration.name"
:subtitle="getIntegrationType(integration).getSummary(integration)"
avatar-color="ghost"
avatar-color="transparent"
style="flex: 1"
/>
<div class="integration_settings__integration-actions">

View file

@ -26,7 +26,7 @@
:image="getUserSourceType(userSource).image"
:title="userSource.name"
:subtitle="getUserSourceType(userSource).getSummary(userSource)"
avatar-color="ghost"
avatar-color="transparent"
style="flex: 1"
/>
<div class="user-source-settings__user-source-actions">
@ -60,9 +60,9 @@
<Presentation
:image="getUserSourceType(editedUserSource).image"
:title="getUserSourceType(editedUserSource).name"
avatar-color="ghost"
avatar-color="transparent"
style="flex: 1; margin-bottom: 18px"
icon-size="tiny"
icon-size="medium"
/>
<UpdateUserSourceForm

View file

@ -26,7 +26,35 @@
:default-values="defaultValues"
:application="builder"
:integration="integration"
@values-changed="emitChange"
/>
<div v-if="integration">
<h4>{{ $t('updateUserSourceForm.authTitle') }}</h4>
<div v-for="appAuthType in appAuthProviderTypes" :key="appAuthType.type">
<Checkbox
:checked="hasAtLeastOneOfThisType(appAuthType)"
@input="onSelect(appAuthType)"
>
{{ appAuthType.name }}
</Checkbox>
<div
v-for="appAuthProvider in appAuthProviderPerTypes[appAuthType.type]"
:key="appAuthProvider.id"
class="update-user-source-form__auth-provider-form"
>
<component
:is="appAuthType.formComponent"
v-if="hasAtLeastOneOfThisType(appAuthType)"
:integration="integration"
:current-user-source="fullValues"
:default-values="appAuthProvider"
excluded-form
@values-changed="updateAuthProvider(appAuthProvider, $event)"
/>
</div>
</div>
</div>
</form>
</template>
@ -34,6 +62,7 @@
import form from '@baserow/modules/core/mixins/form'
import IntegrationDropdown from '@baserow/modules/core/components/integrations/IntegrationDropdown'
import { required, maxLength } from 'vuelidate/lib/validators'
import { uuid } from '@baserow/modules/core/utils/string'
export default {
components: { IntegrationDropdown },
@ -58,8 +87,9 @@ export default {
values: {
integration_id: null,
name: '',
auth_providers: [],
},
allowedValues: ['integration_id', 'name'],
fullValues: this.getFormValues(),
}
},
computed: {
@ -71,8 +101,67 @@ export default {
({ id }) => id === this.values.integration_id
)
},
appAuthProviderTypes() {
return this.$registry.getOrderedList('appAuthProvider')
},
appAuthProviderPerTypes() {
return Object.fromEntries(
this.appAuthProviderTypes.map((authType) => {
return [
authType.type,
this.values.auth_providers.filter(
({ type }) => type === authType.type
),
]
})
)
},
},
methods: {
// Override the default getChildFormValues to exclude the provider forms from
// final values as they are handled directly by this component
getChildFormsValues() {
return Object.assign(
{},
...this.$children
.filter(
(child) =>
'getChildFormsValues' in child &&
child.$attrs['excluded-form'] === undefined
)
.map((child) => {
return child.getFormValues()
})
)
},
hasAtLeastOneOfThisType(appAuthProviderType) {
return this.appAuthProviderPerTypes[appAuthProviderType.type]?.length > 0
},
onSelect(appAuthProviderType) {
if (this.hasAtLeastOneOfThisType(appAuthProviderType)) {
this.values.auth_providers = this.values.auth_providers.filter(
({ type }) => type !== appAuthProviderType.type
)
} else {
this.values.auth_providers.push({
type: appAuthProviderType.type,
id: uuid(),
})
}
},
updateAuthProvider(authProviderToChange, values) {
this.values.auth_providers = this.values.auth_providers.map(
(authProvider) => {
if (authProvider.id === authProviderToChange.id) {
return { ...authProvider, ...values }
}
return authProvider
}
)
},
emitChange() {
this.fullValues = this.getFormValues()
},
getError(fieldName) {
if (!this.$v.values[fieldName].$dirty) {
return ''

View file

@ -440,6 +440,7 @@
"updateUserSourceForm": {
"nameFieldLabel": "Name",
"nameFieldPlaceholder": "Enter a name...",
"authTitle": "Authentication",
"integrationFieldLabel": "Integration"
},
"formContainerElementForm": {

View file

@ -9,6 +9,7 @@ import {
DomainsBuilderSettingsType,
IntegrationsBuilderSettingsType,
ThemeBuilderSettingsType,
UserSourcesBuilderSettingsType,
} from '@baserow/modules/builder/builderSettingTypes'
import pageStore from '@baserow/modules/builder/store/page'
@ -150,6 +151,13 @@ export default (context) => {
new DomainsBuilderSettingsType(context)
)
if (app.$featureFlagIsEnabled('builder-user-source')) {
app.$registry.register(
'builderSettings',
new UserSourcesBuilderSettingsType(context)
)
}
app.$registry.register('errorPage', new PublicSiteErrorPageType(context))
app.$registry.register('element', new HeadingElementType(context))

View file

@ -0,0 +1,18 @@
import { Registerable } from '@baserow/modules/core/registry'
export class AppAuthProviderType extends Registerable {
get name() {
throw new Error('Must be set on the type.')
}
/**
* The form to edit this user source.
*/
get formComponent() {
return null
}
getOrder() {
return 0
}
}

View file

@ -26,3 +26,4 @@
@import 'workflow_action_selector';
@import 'user_source_settings';
@import 'loading_spinner';
@import 'update_user_source_form';

View file

@ -0,0 +1,3 @@
.update-user-source-form__auth-provider-form {
margin-left: 24px;
}

View file

@ -59,8 +59,10 @@ export default {
required: false,
type: String,
default: 'blue',
validator: function (value) {
validator(value) {
return [
null,
undefined,
'blue',
'cyan',
'green',
@ -80,7 +82,7 @@ export default {
type: String,
required: false,
default: 'medium',
validator: function (value) {
validator(value) {
return ['small', 'medium', 'large', 'x-large'].includes(value)
},
},

View file

@ -26,7 +26,7 @@ export default {
}
},
mounted() {
Object.assign(this.values, this.values, this.getDefaultValues())
this.values = Object.assign({}, this.values, this.getDefaultValues())
},
watch: {
values: {
@ -132,7 +132,8 @@ export default {
* calling the submitted event.
*/
getFormValues() {
return Object.assign({}, this.values, this.getChildFormsValues())
const result = Object.assign({}, this.values, this.getChildFormsValues())
return result
},
/**
* Returns an object containing the values of the child forms.

View file

@ -52,6 +52,7 @@ import sidebarStore from '@baserow/modules/core/store/sidebar'
import undoRedoStore from '@baserow/modules/core/store/undoRedo'
import integrationStore from '@baserow/modules/core/store/integration'
import userSourceStore from '@baserow/modules/core/store/userSource'
import appAuthProviderStore from '@baserow/modules/core/store/appAuthProvider'
import notificationStore from '@baserow/modules/core/store/notification'
import en from '@baserow/modules/core/locales/en.json'
@ -101,6 +102,11 @@ export default (context, inject) => {
registry.registerNamespace('runtimeFormulaFunction')
registry.registerNamespace('notification')
registry.registerNamespace('workflowAction')
registry.registerNamespace('integration')
registry.registerNamespace('service')
registry.registerNamespace('userSource')
registry.registerNamespace('appAuthProvider')
registry.register('settings', new AccountSettingsType(context))
registry.register('settings', new PasswordSettingsType(context))
registry.register('settings', new EmailNotificationsSettingsType(context))
@ -142,10 +148,7 @@ export default (context, inject) => {
store.registerModule('undoRedo', undoRedoStore)
store.registerModule('integration', integrationStore)
store.registerModule('userSource', userSourceStore)
registry.registerNamespace('integration')
registry.registerNamespace('service')
registry.registerNamespace('userSource')
store.registerModule('appAuthProvider', appAuthProviderStore)
store.registerModule('notification', notificationStore)
registry.register('authProvider', new PasswordAuthProviderType(context))

View file

@ -0,0 +1,24 @@
export default (client) => {
return {
fetchAll(userSourceId) {
return client.get(`user_source/${userSourceId}/app_auth_providers/`)
},
create(userSourceId, appAuthProviderType, values) {
const payload = {
type: appAuthProviderType,
...values,
}
return client.post(
`user_source/${userSourceId}/app_auth_providers/`,
payload
)
},
update(appAuthProviderId, values) {
return client.patch(`app_auth_provider/${appAuthProviderId}/`, values)
},
delete(appAuthProviderId) {
return client.delete(`app_auth_provider/${appAuthProviderId}/`)
},
}
}

View file

@ -0,0 +1,198 @@
import AppAuthProviderService from '@baserow/modules/core/services/appAuthProvider'
const state = {}
const updateContext = {
updateTimeout: null,
promiseResolve: null,
lastUpdatedValues: null,
}
const mutations = {
ADD_ITEM(state, { userSource, appAuthProvider, beforeId = null }) {
if (beforeId === null) {
userSource.appAuthProviders.push(appAuthProvider)
} else {
const insertionIndex = userSource.appAuthProviders.findIndex(
(e) => e.id === beforeId
)
userSource.appAuthProviders.splice(insertionIndex, 0, appAuthProvider)
}
},
UPDATE_ITEM(
state,
{ userSource, appAuthProvider: appAuthProviderToUpdate, values }
) {
userSource.appAuthProviders.forEach((appAuthProvider) => {
if (appAuthProvider.id === appAuthProviderToUpdate.id) {
Object.assign(appAuthProvider, values)
}
})
},
DELETE_ITEM(state, { userSource, appAuthProviderId }) {
const index = userSource.appAuthProviders.findIndex(
(appAuthProvider) => appAuthProvider.id === appAuthProviderId
)
if (index > -1) {
userSource.appAuthProviders.splice(index, 1)
}
},
CLEAR_ITEMS(state, { userSource }) {
userSource.appAuthProviders = []
},
}
const actions = {
forceCreate({ commit }, { userSource, appAuthProvider, beforeId = null }) {
commit('ADD_ITEM', { userSource, appAuthProvider, beforeId })
},
forceUpdate({ commit }, { userSource, appAuthProvider, values }) {
commit('UPDATE_ITEM', { userSource, appAuthProvider, values })
},
forceDelete({ commit, getters }, { userSource, appAuthProviderId }) {
commit('DELETE_ITEM', { userSource, appAuthProviderId })
},
async create({ dispatch }, { userSource, appAuthProviderType, values }) {
const { data: appAuthProvider } = await AppAuthProviderService(
this.$client
).create(userSource.id, appAuthProviderType, values)
await dispatch('forceCreate', { userSource, appAuthProvider })
return appAuthProvider
},
async update(
{ dispatch, getters },
{ userSource, appAuthProviderId, values }
) {
const appAuthProvidersOfUserSource = getters.getUserSources(userSource)
const appAuthProvider = appAuthProvidersOfUserSource.find(
({ id }) => id === appAuthProviderId
)
const oldValues = {}
const newValues = {}
Object.keys(values).forEach((name) => {
if (Object.prototype.hasOwnProperty.call(appAuthProvider, name)) {
oldValues[name] = appAuthProvider[name]
newValues[name] = values[name]
}
})
await dispatch('forceUpdate', {
userSource,
appAuthProvider,
values: newValues,
})
try {
await AppAuthProviderService(this.$client).update(
appAuthProvider.id,
values
)
} catch (error) {
await dispatch('forceUpdate', {
userSource,
appAuthProvider,
values: oldValues,
})
throw error
}
},
async debouncedUpdate(
{ dispatch, getters },
{ userSource, appAuthProviderId, values }
) {
const appAuthProvider = getters.getAppAuthProviders.find(
({ id }) => id === appAuthProviderId
)
const oldValues = {}
const newValues = {}
Object.keys(values).forEach((name) => {
if (Object.prototype.hasOwnProperty.call(appAuthProvider, name)) {
oldValues[name] = appAuthProvider[name]
newValues[name] = values[name]
}
})
await dispatch('forceUpdate', {
userSource,
appAuthProvider,
values: newValues,
})
return new Promise((resolve, reject) => {
const fire = async () => {
try {
await AppAuthProviderService(this.$client).update(
appAuthProvider.id,
values
)
updateContext.lastUpdatedValues = values
resolve()
} catch (error) {
// Revert to old values on error
await dispatch('forceUpdate', {
userSource,
appAuthProvider,
values: updateContext.lastUpdatedValues,
})
reject(error)
}
}
if (updateContext.promiseResolve) {
updateContext.promiseResolve()
updateContext.promiseResolve = null
}
clearTimeout(updateContext.updateTimeout)
if (!updateContext.lastUpdatedValues) {
updateContext.lastUpdatedValues = oldValues
}
updateContext.updateTimeout = setTimeout(fire, 500)
updateContext.promiseResolve = resolve
})
},
async delete({ dispatch, getters }, { userSource, appAuthProviderId }) {
const appAuthProvidersOfUserSource = getters.getUserSources(userSource)
const appAuthProviderIndex = appAuthProvidersOfUserSource.findIndex(
(appAuthProvider) => appAuthProvider.id === appAuthProviderId
)
const appAuthProviderToDelete =
appAuthProvidersOfUserSource[appAuthProviderIndex]
const beforeId =
appAuthProviderIndex !== appAuthProvidersOfUserSource.length - 1
? appAuthProvidersOfUserSource[appAuthProviderIndex + 1].id
: null
await dispatch('forceDelete', { userSource, appAuthProviderId })
try {
await AppAuthProviderService(this.$client).delete(appAuthProviderId)
} catch (error) {
await dispatch('forceCreate', {
userSource,
appAuthProvider: appAuthProviderToDelete,
beforeId,
})
throw error
}
},
}
const getters = {
getAppAuthProviders: (state) => (userSource) => {
return userSource.appAuthProviders
},
}
export default {
namespaced: true,
state,
getters,
actions,
mutations,
}

View file

@ -1,4 +1,5 @@
import UserSourceService from '@baserow/modules/core/services/userSource'
import _ from 'lodash'
const state = {
// The loaded userSources
@ -90,10 +91,12 @@ const actions = {
async update({ dispatch, getters }, { userSourceId, values }) {
const userSourcesOfPage = getters.getUserSources
const userSource = userSourcesOfPage.find(({ id }) => id === userSourceId)
const oldValues = {}
const newValues = {}
Object.keys(values).forEach((name) => {
if (Object.prototype.hasOwnProperty.call(userSource, name)) {
if (!_.isEqual(userSource[name], values[name])) {
oldValues[name] = userSource[name]
newValues[name] = values[name]
}
@ -102,7 +105,10 @@ const actions = {
await dispatch('forceUpdate', { userSource, values: newValues })
try {
await UserSourceService(this.$client).update(userSource.id, values)
const { data: newUserSource } = await UserSourceService(
this.$client
).update(userSource.id, newValues)
await dispatch('forceUpdate', { userSource, values: newUserSource })
} catch (error) {
await dispatch('forceUpdate', { userSource, values: oldValues })
throw error

View file

@ -9,7 +9,7 @@
:title="values.authorized_user.first_name"
:subtitle="values.authorized_user.username"
:initials="values.authorized_user.first_name | nameAbbreviation"
avatar-color="primary"
avatar-color="blue"
/>
<div>{{ $t('localBaserowForm.userMessage') }}</div>
</div>