A tool for syncing a directory (AD, LDAP, Azure, G Suite, Okta) to an organization.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

755 lines
28 KiB

<div class="row g-3">
<div class="col-sm">
<div class="card mb-3">
<h3 class="card-header">{{ "directory" | i18n }}</h3>
<div class="card-body">
<div class="mb-3">
<label for="directory" class="form-label">{{ "type" | i18n }}</label>
<select class="form-select" id="directory" name="Directory" [(ngModel)]="directory">
<option *ngFor="let o of directoryOptions" [ngValue]="o.value">
{{ o.name }}
</option>
</select>
</div>
<div [hidden]="directory != directoryType.Ldap">
<div class="mb-3">
<label for="hostname" class="form-label">{{ "serverHostname" | i18n }}</label>
<input
type="text"
class="form-control"
id="hostname"
name="Hostname"
[(ngModel)]="ldap.hostname"
/>
<div class="form-text">{{ "ex" | i18n }} ad.company.com</div>
</div>
<div class="mb-3">
<label for="port" class="form-label">{{ "port" | i18n }}</label>
<input type="text" class="form-control" id="port" name="Port" [(ngModel)]="ldap.port" />
<div class="form-text">{{ "ex" | i18n }} 389</div>
</div>
<div class="mb-3">
<label for="rootPath" class="form-label">{{ "rootPath" | i18n }}</label>
<input
type="text"
class="form-control"
id="rootPath"
name="RootPath"
[(ngModel)]="ldap.rootPath"
/>
<div class="form-text">{{ "ex" | i18n }} dc=company,dc=com</div>
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="ad"
[(ngModel)]="ldap.ad"
name="AD"
/>
<label class="form-check-label" for="ad">{{ "ldapAd" | i18n }}</label>
</div>
</div>
<div class="mb-3" *ngIf="!ldap.ad">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="pagedSearch"
[(ngModel)]="ldap.pagedSearch"
name="PagedSearch"
/>
<label class="form-check-label" for="pagedSearch">{{
"ldapPagedResults" | i18n
}}</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="ldapEncrypted"
[(ngModel)]="ldap.ssl"
name="Encrypted"
/>
<label class="form-check-label" for="ldapEncrypted">{{
"ldapEncrypted" | i18n
}}</label>
</div>
</div>
<div class="ms-4" *ngIf="ldap.ssl">
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="radio"
[value]="false"
id="ssl"
[(ngModel)]="ldap.startTls"
name="SSL"
/>
<label class="form-check-label" for="ssl">{{ "ldapSsl" | i18n }}</label>
</div>
<div class="form-check">
<input
class="form-check-input"
type="radio"
[value]="true"
id="startTls"
[(ngModel)]="ldap.startTls"
name="StartTLS"
/>
<label class="form-check-label" for="startTls">{{ "ldapTls" | i18n }}</label>
</div>
</div>
<div class="ms-4" *ngIf="ldap.startTls">
<p>{{ "ldapTlsUntrustedDesc" | i18n }}</p>
<div class="mb-3">
<label for="tlsCaPath" class="form-label">{{ "ldapTlsCa" | i18n }}</label>
<input
type="file"
class="form-control mb-2"
id="tlsCaPath_file"
(change)="setSslPath('tlsCaPath')"
/>
<input
type="text"
class="form-control"
id="tlsCaPath"
name="TLSCaPath"
[(ngModel)]="ldap.tlsCaPath"
/>
</div>
</div>
<div class="ms-4" *ngIf="!ldap.startTls">
<p>{{ "ldapSslUntrustedDesc" | i18n }}</p>
<div class="mb-3">
<label for="sslCertPath" class="form-label">{{ "ldapSslCert" | i18n }}</label>
<input
type="file"
class="form-control mb-2"
id="sslCertPath_file"
(change)="setSslPath('sslCertPath')"
/>
<input
type="text"
class="form-control"
id="sslCertPath"
name="SSLCertPath"
[(ngModel)]="ldap.sslCertPath"
/>
</div>
<div class="mb-3">
<label for="sslKeyPath" class="form-label">{{ "ldapSslKey" | i18n }}</label>
<input
type="file"
class="form-control mb-2"
id="sslKeyPath_file"
(change)="setSslPath('sslKeyPath')"
/>
<input
type="text"
class="form-control"
id="sslKeyPath"
name="SSLKeyPath"
[(ngModel)]="ldap.sslKeyPath"
/>
</div>
<div class="mb-3">
<label for="sslCaPath" class="form-label">{{ "ldapSslCa" | i18n }}</label>
<input
type="file"
class="form-control mb-2"
id="sslCaPath_file"
(change)="setSslPath('sslCaPath')"
/>
<input
type="text"
class="form-control"
id="sslCaPath"
name="SSLCaPath"
[(ngModel)]="ldap.sslCaPath"
/>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="certDoNotVerify"
[(ngModel)]="ldap.sslAllowUnauthorized"
name="CertDoNoVerify"
/>
<label class="form-check-label" for="certDoNotVerify">{{
"ldapCertDoNotVerify" | i18n
}}</label>
</div>
</div>
</div>
<div class="mb-3" [hidden]="true">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="currentUser"
[(ngModel)]="ldap.currentUser"
name="CurrentUser"
/>
<label class="form-check-label" for="currentUser">{{ "currentUser" | i18n }}</label>
</div>
</div>
<div [hidden]="ldap.currentUser">
<div class="mb-3">
<label for="username" class="form-label">{{ "username" | i18n }}</label>
<input
type="text"
class="form-control"
id="username"
name="Username"
[(ngModel)]="ldap.username"
/>
<div class="form-text" *ngIf="ldap.ad">{{ "ex" | i18n }} company\admin</div>
<div class="form-text" *ngIf="!ldap.ad">
{{ "ex" | i18n }} cn=admin,dc=company,dc=com
</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">{{ "password" | i18n }}</label>
<div class="input-group">
<input
type="{{ showLdapPassword ? 'text' : 'password' }}"
class="form-control"
id="password"
name="Password"
[(ngModel)]="ldap.password"
/>
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="toggleLdapPassword()"
>
<i
class="bwi bwi-lg"
aria-hidden="true"
[ngClass]="showLdapPassword ? 'bwi-eye-slash' : 'bwi-eye'"
></i>
</button>
</div>
</div>
</div>
</div>
<div [hidden]="directory != directoryType.AzureActiveDirectory">
<div class="mb-3">
<label for="identityAuthority" class="form-label">{{
"identityAuthority" | i18n
}}</label>
<select
class="form-select"
id="identityAuthority"
name="IdentityAuthority"
[(ngModel)]="azure.identityAuthority"
>
<option value="login.microsoftonline.com">Azure AD Public</option>
<option value="login.microsoftonline.us">Azure AD Government</option>
</select>
</div>
<div class="mb-3">
<label for="tenant" class="form-label">{{ "tenant" | i18n }}</label>
<input
type="text"
class="form-control"
id="tenant"
name="Tenant"
[(ngModel)]="azure.tenant"
/>
<div class="form-text">{{ "ex" | i18n }} companyad.onmicrosoft.com</div>
</div>
<div class="mb-3">
<label for="applicationId" class="form-label">{{ "applicationId" | i18n }}</label>
<input
type="text"
class="form-control"
id="applicationId"
name="ApplicationId"
[(ngModel)]="azure.applicationId"
/>
</div>
<div class="mb-3">
<label for="secretKey" class="form-label">{{ "secretKey" | i18n }}</label>
<div class="input-group">
<input
type="{{ showAzureKey ? 'text' : 'password' }}"
class="form-control"
id="secretKey"
name="SecretKey"
[(ngModel)]="azure.key"
/>
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="toggleAzureKey()"
>
<i
class="bwi bwi-lg"
aria-hidden="true"
[ngClass]="showAzureKey ? 'bwi-eye-slash' : 'bwi-eye'"
></i>
</button>
</div>
</div>
</div>
<div [hidden]="directory != directoryType.Okta">
<div class="mb-3">
<label for="orgUrl" class="form-label">{{ "organizationUrl" | i18n }}</label>
<input
type="text"
class="form-control"
id="orgUrl"
name="OrgUrl"
[(ngModel)]="okta.orgUrl"
/>
<div class="form-text">{{ "ex" | i18n }} https://mycompany.okta.com/</div>
</div>
<div class="mb-3">
<label for="oktaToken" class="form-label">{{ "token" | i18n }}</label>
<div class="input-group">
<input
type="{{ showOktaKey ? 'text' : 'password' }}"
class="form-control"
id="oktaToken"
name="OktaToken"
[(ngModel)]="okta.token"
/>
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="toggleOktaKey()"
>
<i
class="bwi bwi-lg"
aria-hidden="true"
[ngClass]="showOktaKey ? 'bwi-eye-slash' : 'bwi-eye'"
></i>
</button>
</div>
</div>
</div>
<div [hidden]="directory != directoryType.OneLogin">
<div class="mb-3">
<label for="oneLoginClientId" class="form-label">{{ "clientId" | i18n }}</label>
<input
type="text"
class="form-control"
id="oneLoginClientId"
name="OneLoginClientId"
[(ngModel)]="oneLogin.clientId"
/>
</div>
<div class="mb-3">
<label for="oneLoginClientSecret" class="form-label">{{ "clientSecret" | i18n }}</label>
<div class="input-group">
<input
type="{{ showOneLoginSecret ? 'text' : 'password' }}"
class="form-control"
id="oneLoginClientSecret"
name="OneLoginClientSecret"
[(ngModel)]="oneLogin.clientSecret"
/>
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="toggleOneLoginSecret()"
>
<i
class="bwi bwi-lg"
aria-hidden="true"
[ngClass]="showOneLoginSecret ? 'bwi-eye-slash' : 'bwi-eye'"
></i>
</button>
</div>
</div>
<div class="mb-3">
<label for="oneLoginRegion" class="form-label">{{ "region" | i18n }}</label>
<select
class="form-select"
id="oneLoginRegion"
name="OneLoginRegion"
[(ngModel)]="oneLogin.region"
>
<option value="us">US</option>
<option value="eu">EU</option>
</select>
</div>
</div>
<div [hidden]="directory != directoryType.GSuite">
<div class="mb-3">
<label for="domain" class="form-label">{{ "domain" | i18n }}</label>
<input
type="text"
class="form-control"
id="domain"
name="Domain"
[(ngModel)]="gsuite.domain"
/>
<div class="form-text">{{ "ex" | i18n }} company.com</div>
</div>
<div class="mb-3">
<label for="adminUser" class="form-label">{{ "adminUser" | i18n }}</label>
<input
type="text"
class="form-control"
id="adminUser"
name="AdminUser"
[(ngModel)]="gsuite.adminUser"
/>
<div class="form-text">{{ "ex" | i18n }} admin&#64;company.com</div>
</div>
<div class="mb-3">
<label for="customerId" class="form-label">{{ "customerId" | i18n }}</label>
<input
type="text"
class="form-control"
id="customerId"
name="CustomerId"
[(ngModel)]="gsuite.customer"
/>
<div class="form-text">{{ "ex" | i18n }} 39204722352</div>
</div>
<div class="mb-3">
<label for="keyFile" class="form-label">{{ "jsonKeyFile" | i18n }}</label>
<input
type="file"
class="form-control"
id="keyFile"
accept=".json"
(change)="parseKeyFile()"
/>
<div class="form-text">{{ "ex" | i18n }} My Project-jksd3jd223.json</div>
</div>
<div class="mb-3" [hidden]="!gsuite.clientEmail">
<label for="clientEmail" class="form-label">{{ "clientEmail" | i18n }}</label>
<input
type="text"
class="form-control"
id="clientEmail"
name="ClientEmail"
[(ngModel)]="gsuite.clientEmail"
/>
</div>
<div class="mb-3" [hidden]="!gsuite.privateKey">
<label for="privateKey" class="form-label">{{ "privateKey" | i18n }}</label>
<textarea
class="form-control text-monospace"
id="privateKey"
name="PrivateKey"
[(ngModel)]="gsuite.privateKey"
>
</textarea>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm">
<div class="card">
<h3 class="card-header">{{ "sync" | i18n }}</h3>
<div class="card-body">
<div class="mb-3">
<label for="interval" class="form-label">{{ "interval" | i18n }}</label>
<input
type="number"
min="5"
class="form-control"
id="interval"
name="Interval"
[(ngModel)]="sync.interval"
/>
<div class="form-text">{{ "intervalMin" | i18n }}</div>
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="removeDisabled"
[(ngModel)]="sync.removeDisabled"
name="RemoveDisabled"
/>
<label class="form-check-label" for="removeDisabled">{{
"removeDisabled" | i18n
}}</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="overwriteExisting"
[(ngModel)]="sync.overwriteExisting"
name="OverwriteExisting"
/>
<label class="form-check-label" for="overwriteExisting">{{
"overwriteExisting" | i18n
}}</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="largeImport"
[(ngModel)]="sync.largeImport"
name="LargeImport"
/>
<label class="form-check-label" for="largeImport">{{ "largeImport" | i18n }}</label>
</div>
</div>
<div [hidden]="directory != directoryType.Ldap">
<div [hidden]="ldap.ad">
<div class="mb-3">
<label for="memberAttribute" class="form-label">{{ "memberAttribute" | i18n }}</label>
<input
type="text"
class="form-control"
id="memberAttribute"
name="MemberAttribute"
[(ngModel)]="sync.memberAttribute"
/>
<div class="form-text">{{ "ex" | i18n }} uniqueMember</div>
</div>
<div class="mb-3">
<label for="creationDateAttribute" class="form-label">{{
"creationDateAttribute" | i18n
}}</label>
<input
type="text"
class="form-control"
id="creationDateAttribute"
name="CreationDateAttribute"
[(ngModel)]="sync.creationDateAttribute"
/>
<div class="form-text">{{ "ex" | i18n }} whenCreated</div>
</div>
<div class="mb-3">
<label for="revisionDateAttribute" class="form-label">{{
"revisionDateAttribute" | i18n
}}</label>
<input
type="text"
class="form-control"
id="revisionDateAttribute"
name="RevisionDateAttribute"
[(ngModel)]="sync.revisionDateAttribute"
/>
<div class="form-text">{{ "ex" | i18n }} whenChanged</div>
</div>
</div>
</div>
<div [hidden]="directory != directoryType.Ldap && directory != directoryType.OneLogin">
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="useEmailPrefixSuffix"
[(ngModel)]="sync.useEmailPrefixSuffix"
name="UseEmailPrefixSuffix"
/>
<label class="form-check-label" for="useEmailPrefixSuffix">{{
"useEmailPrefixSuffix" | i18n
}}</label>
</div>
</div>
<div [hidden]="!sync.useEmailPrefixSuffix">
<div class="mb-3" [hidden]="ldap.ad || directory != directoryType.Ldap">
<label for="emailPrefixAttribute" class="form-label">{{
"emailPrefixAttribute" | i18n
}}</label>
<input
type="text"
class="form-control"
id="emailPrefixAttribute"
name="EmailPrefixAttribute"
[(ngModel)]="sync.emailPrefixAttribute"
/>
<div class="form-text">{{ "ex" | i18n }} accountName</div>
</div>
<div class="mb-3">
<label for="emailSuffix" class="form-label">{{ "emailSuffix" | i18n }}</label>
<input
type="text"
class="form-control"
id="emailSuffix"
name="EmailSuffix"
[(ngModel)]="sync.emailSuffix"
/>
<div class="form-text">{{ "ex" | i18n }} &#64;company.com</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="syncUsers"
[(ngModel)]="sync.users"
name="SyncUsers"
/>
<label class="form-check-label" for="syncUsers">{{ "syncUsers" | i18n }}</label>
</div>
</div>
<div [hidden]="!sync.users">
<div class="mb-3">
<label for="userFilter" class="form-label">{{ "userFilter" | i18n }}</label>
<textarea
class="form-control"
id="userFilter"
name="UserFilter"
[(ngModel)]="sync.userFilter"
></textarea>
<div class="form-text" *ngIf="directory === directoryType.Ldap">
{{ "ex" | i18n }} (&amp;(givenName=John)(|(l=Dallas)(l=Austin)))
</div>
<div class="form-text" *ngIf="directory === directoryType.AzureActiveDirectory">
{{ "ex" | i18n }} exclude:joe&#64;company.com
</div>
<div class="form-text" *ngIf="directory === directoryType.Okta">
{{ "ex" | i18n }} exclude:joe&#64;company.com | profile.firstName eq "John"
</div>
<div class="form-text" *ngIf="directory === directoryType.GSuite">
{{ "ex" | i18n }} exclude:joe&#64;company.com | orgName=Engineering
</div>
</div>
<div class="mb-3" [hidden]="directory != directoryType.Ldap">
<label for="userPath" class="form-label">{{ "userPath" | i18n }}</label>
<input
type="text"
class="form-control"
id="userPath"
name="UserPath"
[(ngModel)]="sync.userPath"
/>
<div class="form-text">{{ "ex" | i18n }} CN=Users</div>
</div>
<div [hidden]="directory != directoryType.Ldap || ldap.ad">
<div class="mb-3">
<label for="userObjectClass" class="form-label">{{ "userObjectClass" | i18n }}</label>
<input
type="text"
class="form-control"
id="userObjectClass"
name="UserObjectClass"
[(ngModel)]="sync.userObjectClass"
/>
<div class="form-text">{{ "ex" | i18n }} inetOrgPerson</div>
</div>
<div class="mb-3">
<label for="userEmailAttribute" class="form-label">{{
"userEmailAttribute" | i18n
}}</label>
<input
type="text"
class="form-control"
id="userEmailAttribute"
name="UserEmailAttribute"
[(ngModel)]="sync.userEmailAttribute"
/>
<div class="form-text">{{ "ex" | i18n }} mail</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="syncGroups"
[(ngModel)]="sync.groups"
name="SyncGroups"
/>
<label class="form-check-label" for="syncGroups">{{
(directory !== directoryType.OneLogin ? "syncGroups" : "syncGroupsOneLogin") | i18n
}}</label>
</div>
</div>
<div [hidden]="!sync.groups">
<div class="mb-3">
<label for="groupFilter" class="form-label">{{
(directory !== directoryType.OneLogin ? "groupFilter" : "groupFilterOneLogin") | i18n
}}</label>
<textarea
class="form-control"
id="groupFilter"
name="GroupFilter"
[(ngModel)]="sync.groupFilter"
></textarea>
<div class="form-text" *ngIf="directory === directoryType.Ldap">
{{ "ex" | i18n }} (&amp;(objectClass=group)(!(cn=Sales*))(!(cn=IT*)))
</div>
<div class="form-text" *ngIf="directory === directoryType.AzureActiveDirectory">
{{ "ex" | i18n }} include:Sales,IT
</div>
<div class="form-text" *ngIf="directory === directoryType.Okta">
{{ "ex" | i18n }} include:Sales,IT | type eq "APP_GROUP"
</div>
<div class="form-text" *ngIf="directory === directoryType.GSuite">
{{ "ex" | i18n }} include:Sales,IT
</div>
</div>
<div class="mb-3" [hidden]="directory != directoryType.Ldap">
<label for="groupPath" class="form-label">{{ "groupPath" | i18n }}</label>
<input
type="text"
class="form-control"
id="groupPath"
name="GroupPath"
[(ngModel)]="sync.groupPath"
/>
<div class="form-text" *ngIf="!ldap.ad">{{ "ex" | i18n }} CN=Groups</div>
<div class="form-text" *ngIf="ldap.ad">{{ "ex" | i18n }} CN=Users</div>
</div>
<div [hidden]="directory != directoryType.Ldap || ldap.ad">
<div class="mb-3">
<label for="groupObjectClass" class="form-label">{{
"groupObjectClass" | i18n
}}</label>
<input
type="text"
class="form-control"
id="groupObjectClass"
name="GroupObjectClass"
[(ngModel)]="sync.groupObjectClass"
/>
<div class="form-text">{{ "ex" | i18n }} groupOfUniqueNames</div>
</div>
<div class="mb-3">
<label for="groupNameAttribute" class="form-label">{{
"groupNameAttribute" | i18n
}}</label>
<input
type="text"
class="form-control"
id="groupNameAttribute"
name="GroupNameAttribute"
[(ngModel)]="sync.groupNameAttribute"
/>
<div class="form-text">{{ "ex" | i18n }} name</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>