Add App setup wizard
This commit is contained in:
parent
ca11e3afae
commit
b275391674
@ -1,4 +1,3 @@
|
|||||||
- App setup wizard
|
|
||||||
- Cobalt form JSON field type - Setting resource
|
- Cobalt form JSON field type - Setting resource
|
||||||
- MFA recovery codes handling
|
- MFA recovery codes handling
|
||||||
- Forgot password handling
|
- Forgot password handling
|
||||||
|
@ -14,10 +14,10 @@ const template = `
|
|||||||
</span>
|
</span>
|
||||||
<span v-if="can_access">
|
<span v-if="can_access">
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-10"><h3>{{ resource_class.plural }}</h3></div>
|
<div class="col-8"><h3>{{ resource_class.plural }}</h3></div>
|
||||||
<div class="col-2 text-right" v-if="definition.actions">
|
<div class="col-4 text-right" v-if="definition.actions">
|
||||||
<button
|
<button
|
||||||
:class="['btn', 'btn-'+(action.color || 'secondary'), 'btn-sm']"
|
:class="['mr-2', 'btn', 'btn-'+(action.color || 'secondary'), 'btn-sm']"
|
||||||
type="button"
|
type="button"
|
||||||
v-for="action of definition.actions"
|
v-for="action of definition.actions"
|
||||||
@click="perform($event, action)"
|
@click="perform($event, action)"
|
||||||
|
@ -4,6 +4,7 @@ import MessageContainerComponent from './dash/message/MessageContainer.component
|
|||||||
import EditProfileComponent from './dash/profile/EditProfile.component.js'
|
import EditProfileComponent from './dash/profile/EditProfile.component.js'
|
||||||
import AppPasswordFormComponent from './dash/profile/form/AppPassword.component.js'
|
import AppPasswordFormComponent from './dash/profile/form/AppPassword.component.js'
|
||||||
import ProfilePhotoUploaderComponent from './dash/profile/form/ProfilePhotoUploader.component.js'
|
import ProfilePhotoUploaderComponent from './dash/profile/form/ProfilePhotoUploader.component.js'
|
||||||
|
import AppSetupComponent from './dash/AppSetup.component.js'
|
||||||
|
|
||||||
import ListingComponent from './cobalt/Listing.component.js'
|
import ListingComponent from './cobalt/Listing.component.js'
|
||||||
import FormComponent from './cobalt/Form.component.js'
|
import FormComponent from './cobalt/Form.component.js'
|
||||||
@ -15,6 +16,7 @@ const dash_components = {
|
|||||||
EditProfileComponent,
|
EditProfileComponent,
|
||||||
AppPasswordFormComponent,
|
AppPasswordFormComponent,
|
||||||
ProfilePhotoUploaderComponent,
|
ProfilePhotoUploaderComponent,
|
||||||
|
AppSetupComponent,
|
||||||
|
|
||||||
ListingComponent,
|
ListingComponent,
|
||||||
FormComponent,
|
FormComponent,
|
||||||
|
386
app/assets/app/dash/AppSetup.component.js
Normal file
386
app/assets/app/dash/AppSetup.component.js
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
import { Component } from '../../lib/vues6/vues6.js'
|
||||||
|
import { resource_service } from '../service/Resource.service.js'
|
||||||
|
import { location_service } from '../service/Location.service.js'
|
||||||
|
import { session } from '../service/Session.service.js'
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
<div class="card col-12 col-sm-10 offset-sm-1 col-md-8 offset-md-2 col-xl-6 offset-xl-3 mb-5">
|
||||||
|
<div class="card-title m-3 mt-4"><h3>Application Setup Wizard</h3></div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div v-if="step === 0">
|
||||||
|
<p>This wizard will walk you through setting up a new application to integrate with {{ app_name }}. This will allow you to grant {{ app_name }} users access to this application.</p>
|
||||||
|
<p>{{ app_name }} supports 3 different authentication schemas. The application you are setting up will need to support one of the following:</p>
|
||||||
|
<ul>
|
||||||
|
<li>OAuth2</li>
|
||||||
|
<li>SAML</li>
|
||||||
|
<li>LDAP</li>
|
||||||
|
</ul>
|
||||||
|
<p>If the application supports any of these, it can be integrated with {{ app_name }} to provide single-sign-on. All of these methods support {{ app_name }}'s IAM policy, but OAuth2 and SAML2.0 are preferred, because they support a web-based login flow. To get started, enter the application name and identifier:</p>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
v-model="name"
|
||||||
|
placeholder="Awesome External App"
|
||||||
|
@keyup="on_key_up"
|
||||||
|
autofocus
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="app_id"
|
||||||
|
type="text"
|
||||||
|
class="form-control mt-3 mb-3"
|
||||||
|
v-model="identifier"
|
||||||
|
placeholder="awesome_external_app"
|
||||||
|
@keyup="on_key_up"
|
||||||
|
>
|
||||||
|
<small class="text-secondary pad-top">An app's identifier is how it is referenced in IAM configurations. This should preferrably be all lowercase, alphanumeric with underscores.</small>
|
||||||
|
</div>
|
||||||
|
<div v-if="step === 1">
|
||||||
|
Okay, we'll help you set up {{ name }}. What type of authentication does it support?
|
||||||
|
<button class="btn btn-block btn-outline-success mt-4 mb-3" @click="on_type_click('oauth')">
|
||||||
|
OAuth2
|
||||||
|
<br><small>Less common, but best integration. Web-based login flow.</small>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-block btn-outline-primary mb-3" @click="on_type_click('saml')">
|
||||||
|
SAML2.0
|
||||||
|
<br><small>More common in enterprise applications. Web-based login flow.</small>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-block btn-outline-secondary" @click="on_type_click('ldap')">
|
||||||
|
LDAP (BindDN or Simple)
|
||||||
|
<br><small>Most common in self-hosted applications. No web-based login flow.</small>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="step === 2 && type === 'oauth'">
|
||||||
|
<p>We're going to create an OAuth2 client for {{ name }}. This client will have the credentials that {{ name }} will use to authenticate users against {{ app_name }}'s API.</p>
|
||||||
|
<p>By default, the OAuth2 client will be able to fetch information about individual users and groups. You can adjust this in the future by navigating to the OAuth2 Clients page.</p>
|
||||||
|
<p>Please provide the OAuth2 callback URL. This is where {{ app_name }} will redirect users after they have been authenticated.</p>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control mt-3 mb-3"
|
||||||
|
v-model="oauth_redirect_uri"
|
||||||
|
placeholder="https://awesome.app/oauth2/callback"
|
||||||
|
@keyup="on_key_up"
|
||||||
|
>
|
||||||
|
<p>{{ app_name }} only supports the <code>authorization_code</code> grant type.</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="step === 2 && type === 'saml'"
|
||||||
|
>
|
||||||
|
<p>We're going to register {{ name }} as a SAML2.0 service provider. This will allow it to interface with {{ app_name }}.</p>
|
||||||
|
<p>To do this, you need to provide {{ name }}'s entity ID, assertion consumer service URL, and single-logout URL (if supported).</p>
|
||||||
|
<span>Entity ID:</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
v-model="saml_entity_id"
|
||||||
|
placeholder="https://awesome.app/saml/metadata.xml"
|
||||||
|
@keyup="on_key_up"
|
||||||
|
>
|
||||||
|
<br>
|
||||||
|
<span>Assertion Consumer Service URL:</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
v-model="saml_acs_url"
|
||||||
|
placeholder="https://awesome.app/saml/acs"
|
||||||
|
@keyup="on_key_up"
|
||||||
|
>
|
||||||
|
<br>
|
||||||
|
<span>Single-Logout URL (optional):</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
v-model="saml_slo_url"
|
||||||
|
placeholder="https://awesome.app/saml/logout"
|
||||||
|
@keyup="on_key_up"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div v-if="step === 3 && type === 'saml'">
|
||||||
|
<p>Success! {{ name }} was added to {{ app_name }}'s records, and a SAML2.0 service provider was created.</p>
|
||||||
|
<p>The next step is to configure {{ name }} to redirect users to {{ app_name }} to log on. Here's some information on getting it set up:</p>
|
||||||
|
<h4>Configuring the Identity Provider</h4>
|
||||||
|
<p>{{ app_name }} is the SAML2.0 identity provider in this case. To set it up, you'll need the following info:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Entity ID/Metadata: <code>{{ make_url('/saml/metadata.xml') }}</code></li>
|
||||||
|
<li>Sign-On URL: <code>{{ make_url('/saml/sso') }}</code></li>
|
||||||
|
<li>Single-Log-Out URL (if supported): <code>{{ make_url('/saml/logout') }}</code></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-if="step === 2 && type === 'ldap'">
|
||||||
|
<p>We're going to register {{ name }} as an LDAP auth client. To do this, you'll need to specify an LDAP username and password that {{ name }} will use to authenticate users.</p>
|
||||||
|
<span>Username:</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="ldap_username"
|
||||||
|
class='form-control'
|
||||||
|
placeholder="awesome_app_ldap"
|
||||||
|
@keyup="on_key_up"
|
||||||
|
>
|
||||||
|
<br>
|
||||||
|
<span>Password:</span>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
v-model="ldap_password"
|
||||||
|
class='form-control'
|
||||||
|
placeholder="Choose a password."
|
||||||
|
@keyup="on_key_up"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
v-model="ldap_password_confirm"
|
||||||
|
class='form-control'
|
||||||
|
placeholder="Confirm password."
|
||||||
|
@keyup="on_key_up"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div v-if="step === 3 && type === 'ldap'">
|
||||||
|
<p>Success! {{ name }} was added to {{ app_name }}'s records, and an LDAP client was created.</p>
|
||||||
|
<p>The next step is to configure {{ name }} to use {{ app_name }} to log on. Here's some information on getting it set up:</p>
|
||||||
|
<h4>LDAP Credentials</h4>
|
||||||
|
<p>If {{ name }} requires a bind user to query the LDAP server against, you can use these credentials:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Server address: <code>{{ host }}</code></li>
|
||||||
|
<li>Server port: {{ ldap_config.port }}</li>
|
||||||
|
<li>Bind User DN: <code>{{ ldap_config.login_field }}={{ ldap_username }},{{ ldap_config.authentication_base }},{{ ldap_config.base_dc }}</code></li>
|
||||||
|
<li>Password: <i>the password you just set</i></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>User Searching</h4>
|
||||||
|
<ul>
|
||||||
|
<li>User search base: <code>{{ ldap_config.authentication_base }},{{ ldap_config.base_dc }}</code></li>
|
||||||
|
<li>Group search base: <code>{{ ldap_config.group_base }},{{ ldap_config.base_dc }}</code></li>
|
||||||
|
<li>Search filter: <code>(&(objectClass=inetOrgPerson)(iamTarget={{ app.id }})({{ ldap_config.login_field }}=username_substituted_here))</code></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>Group Membership</h4>
|
||||||
|
<p>Groups are made available in a manner compatible with OpenLDAP's memberOf overlay.</p>
|
||||||
|
<p>That means that groups are <code>objectClass: groupOfNames</code> and can be found in the <code>memberOf</code> attribute of the user object.</p>
|
||||||
|
<p>Groups have the form <code>cn=group_name,{{ ldap_config.group_base }},{{ ldap_config.base_dc }}</code>.</p>
|
||||||
|
|
||||||
|
<h4>Other Considerations</h4>
|
||||||
|
<p>{{ app_name }}'s built-in LDAP server provides the minimum-viable level of functionality required to authenticate users. That means it sometimes lacks features that more sophisticated LDAP clients expect.</p>
|
||||||
|
<p>Here are a few settings to tweak:</p>
|
||||||
|
<ul>
|
||||||
|
<li>User display name field: <code>gecos</code> (this is the full name of the user)</li>
|
||||||
|
<li>Paging chunk size: 0 (disable - {{ app_name }} does not support LDAP paging)</li>
|
||||||
|
<li>User e-mail field: <code>mail</code></li>
|
||||||
|
<li>UUID attribute for users: <code>{{ ldap_config.login_field }}</code></li>
|
||||||
|
<li>UUID attribute for groups: <code>cn</code></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-if="step === 3 && type === 'oauth'">
|
||||||
|
<p>Success! {{ name }} was added to {{ app_name }}'s records, and an OAuth2 client was created.</p>
|
||||||
|
<p>The next step is to configure {{ name }} to redirect users to {{ app_name }} to log on. Here's some information on getting it set up:</p>
|
||||||
|
<h4>User Authorization</h4>
|
||||||
|
<p>First, redirect the user to {{ app_name }}. Configure {{ name }} to use this URL:</p>
|
||||||
|
<code>
|
||||||
|
{{ make_url('/auth/service/oauth2/authorize') }}?client_id={{ oauth_client.uuid }}&redirect_uri={{ oauth_client.redirect_url }}
|
||||||
|
</code>
|
||||||
|
<br><br>
|
||||||
|
<p>Once the user authenticates successfully, {{ app_name }} will redirect them back to {{ name }}.</p>
|
||||||
|
|
||||||
|
<h4>Auth Code Redemption</h4>
|
||||||
|
<p>Once the user is redirected back, {{ name }} will be given an authorization code which can be redeemed for a bearer token.</p>
|
||||||
|
<p>To redeem this code, {{ name }} should make a POST request to:</p>
|
||||||
|
<code>
|
||||||
|
{{ make_url('/auth/service/oauth2/redeem') }}
|
||||||
|
</code>
|
||||||
|
<br><br>
|
||||||
|
<p>It should have the following body fields:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>code</code> - the authorization code that was returned</li>
|
||||||
|
<li><code>client_id</code> - <code>{{ oauth_client.uuid }}</code></li>
|
||||||
|
<li><code>client_secret</code> - <code>{{ oauth_client.secret }}</code></li>
|
||||||
|
<li><code>grant_type</code> - <code>authorization_code</code></li>
|
||||||
|
</ul>
|
||||||
|
<p>This will return an <code>access_token</code> that can be used to fetch user information from the {{ app_name }} API.</p>
|
||||||
|
|
||||||
|
<h4>Fetching User Info</h4>
|
||||||
|
<p>Once the auth code has been redeemed for a bearer token, that token can be used to make requests to the {{ app_name }} API.</p>
|
||||||
|
<p>Primarily, it can be used to fetch user information by making a GET request to the following URL:</p>
|
||||||
|
<code>{{ make_url('/api/v1/auth/users/me') }}</code>
|
||||||
|
<br><br>
|
||||||
|
<p>and including the bearer token in the headers like so: <code>Authorization: Bearer AbCdEf124</code></p>
|
||||||
|
|
||||||
|
<h5>Making Test Requests</h5>
|
||||||
|
<p>To test out the API integration, you can generate API tokens for {{ name }}. You can do that by clicking on the User Menu > <a href="/dash/c/listing/reflect/Token" target="_blank">API Tokens</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-right">
|
||||||
|
<span style="color: darkred;" class="font-italic mr-3" v-if="error_message">{{ error_message }}</span>
|
||||||
|
<button
|
||||||
|
v-if="btn_back"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
@click="on_back_click"
|
||||||
|
>Back</button>
|
||||||
|
<button
|
||||||
|
v-if="!btn_hidden"
|
||||||
|
class="btn btn-outline-primary"
|
||||||
|
:disabled="btn_disabled"
|
||||||
|
@click="on_next_click"
|
||||||
|
>Next</button>
|
||||||
|
<button
|
||||||
|
v-if="btn_listing"
|
||||||
|
class="btn btn-outline-success"
|
||||||
|
@click="on_listing_click"
|
||||||
|
>Finish</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
export default class AppSetupComponent extends Component {
|
||||||
|
static get selector() { return 'coreid-app-setup' }
|
||||||
|
static get template() { return template }
|
||||||
|
static get props() { return [] }
|
||||||
|
|
||||||
|
step = 0
|
||||||
|
btn_disabled = true
|
||||||
|
btn_back = false
|
||||||
|
btn_hidden = false
|
||||||
|
btn_listing = false
|
||||||
|
|
||||||
|
name = ''
|
||||||
|
identifier = ''
|
||||||
|
type = '' // ldap | saml | oauth
|
||||||
|
oauth_redirect_uri = ''
|
||||||
|
|
||||||
|
saml_entity_id = ''
|
||||||
|
saml_acs_url = ''
|
||||||
|
saml_slo_url = ''
|
||||||
|
|
||||||
|
ldap_username = ''
|
||||||
|
ldap_password = ''
|
||||||
|
ldap_password_confirm = ''
|
||||||
|
ldap_config = {}
|
||||||
|
|
||||||
|
error_message = ''
|
||||||
|
|
||||||
|
app = {}
|
||||||
|
oauth_client = {}
|
||||||
|
saml_provider = {}
|
||||||
|
ldap_client = {}
|
||||||
|
|
||||||
|
app_name = ''
|
||||||
|
host = ''
|
||||||
|
|
||||||
|
make_url(path) {
|
||||||
|
return session.url(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
async vue_on_create() {
|
||||||
|
this.app_name = session.get('app.name')
|
||||||
|
this.host = session.host()
|
||||||
|
}
|
||||||
|
|
||||||
|
on_key_up(event) {
|
||||||
|
if ( this.step === 0 ) {
|
||||||
|
this.btn_disabled = !this.name.trim() || !this.identifier.trim() || !this.identifier.match(/^([a-z]|[A-Z]|[0-9]|_)+$/)
|
||||||
|
} else if ( this.step === 2 && this.type === 'oauth' ) {
|
||||||
|
this.btn_disabled = !this.oauth_redirect_uri.trim()
|
||||||
|
} else if ( this.step === 2 && this.type === 'saml' ) {
|
||||||
|
this.btn_disabled = !this.saml_entity_id.trim() || !this.saml_acs_url.trim()
|
||||||
|
} else if ( this.step === 2 && this.type === 'ldap' ) {
|
||||||
|
this.btn_disabled = !this.ldap_username.trim() || !this.ldap_username.match(/^([a-z]|[A-Z]|[0-9]|_)+$/) || !this.ldap_password || this.ldap_password !== this.ldap_password_confirm
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( event.keyCode === 13 && !this.btn_disabled ) {
|
||||||
|
// Enter was pressed
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
return this.on_next_click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async on_back_click() {
|
||||||
|
this.step -= 1
|
||||||
|
if ( this.step === 0 ) this.btn_back = false
|
||||||
|
if ( this.step === 0 || this.step === 2 ) this.btn_hidden = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async create_app(merge_params = {}) {
|
||||||
|
const params = {
|
||||||
|
...{
|
||||||
|
name: this.name,
|
||||||
|
identifier: this.identifier,
|
||||||
|
description: '',
|
||||||
|
},
|
||||||
|
...merge_params
|
||||||
|
}
|
||||||
|
|
||||||
|
const app_rsc = await resource_service.get('App')
|
||||||
|
return app_rsc.create(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
async on_next_click() {
|
||||||
|
this.error_message = ''
|
||||||
|
try {
|
||||||
|
if (this.step === 0) {
|
||||||
|
this.step += 1
|
||||||
|
this.btn_hidden = true
|
||||||
|
this.btn_back = true
|
||||||
|
} else if (this.step === 2 && this.type === 'oauth') {
|
||||||
|
const client_rsc = await resource_service.get('oauth/Client')
|
||||||
|
const oauth_client_params = {
|
||||||
|
name: this.name,
|
||||||
|
redirect_url: this.oauth_redirect_uri,
|
||||||
|
api_scopes: ['v1:auth:users:get', 'v1:auth:groups:get'],
|
||||||
|
}
|
||||||
|
|
||||||
|
this.oauth_client = await client_rsc.create(oauth_client_params)
|
||||||
|
this.app = await this.create_app({
|
||||||
|
oauth_client_ids: [this.oauth_client.id]
|
||||||
|
})
|
||||||
|
} else if (this.step === 2 && this.type === 'saml') {
|
||||||
|
const provider_rsc = await resource_service.get('saml/Provider')
|
||||||
|
const provider_params = {
|
||||||
|
name: this.name,
|
||||||
|
acs_url: this.saml_acs_url,
|
||||||
|
entity_id: this.saml_entity_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.saml_slo_url)
|
||||||
|
provider_params.slo_url = this.saml_slo_url
|
||||||
|
|
||||||
|
this.saml_provider = await provider_rsc.create(provider_params)
|
||||||
|
this.app = await this.create_app({
|
||||||
|
saml_service_provider_ids: [this.saml_provider.id]
|
||||||
|
})
|
||||||
|
} else if (this.step === 2 && this.type === 'ldap') {
|
||||||
|
const client_rsc = await resource_service.get('ldap/Client')
|
||||||
|
const client_params = {
|
||||||
|
name: this.name,
|
||||||
|
uid: this.ldap_username,
|
||||||
|
password: this.ldap_password,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ldap_config = await client_rsc.server_config()
|
||||||
|
this.ldap_client = await client_rsc.create(client_params)
|
||||||
|
this.app = await this.create_app({
|
||||||
|
ldap_client_ids: [this.ldap_client.id]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.step === 2 ) {
|
||||||
|
this.step += 1
|
||||||
|
this.btn_disabled = true
|
||||||
|
this.btn_hidden = true
|
||||||
|
this.btn_listing = true
|
||||||
|
this.btn_back = false
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if ( e.response && e.response.data && e.response.data.message )
|
||||||
|
this.error_message = e.response.data.message
|
||||||
|
else this.error_message = 'An unknown error occurred while saving the form.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async on_type_click(type) {
|
||||||
|
this.type = type
|
||||||
|
this.step += 1
|
||||||
|
this.btn_hidden = false
|
||||||
|
this.btn_disabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async on_listing_click() {
|
||||||
|
await location_service.back()
|
||||||
|
}
|
||||||
|
}
|
@ -32,8 +32,15 @@ class AppResource extends CRUDBase {
|
|||||||
type: 'resource',
|
type: 'resource',
|
||||||
position: 'main',
|
position: 'main',
|
||||||
action: 'insert',
|
action: 'insert',
|
||||||
text: 'Create New',
|
text: 'Manual Setup',
|
||||||
|
color: 'outline-success',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
position: 'main',
|
||||||
|
action: 'redirect',
|
||||||
|
text: 'Setup Wizard',
|
||||||
color: 'success',
|
color: 'success',
|
||||||
|
next: '/dash/app/setup',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'resource',
|
type: 'resource',
|
||||||
|
@ -9,6 +9,11 @@ class ClientResource extends CRUDBase {
|
|||||||
item = 'LDAP Client'
|
item = 'LDAP Client'
|
||||||
plural = 'LDAP Clients'
|
plural = 'LDAP Clients'
|
||||||
|
|
||||||
|
async server_config() {
|
||||||
|
const results = await axios.get('/api/v1/ldap/config')
|
||||||
|
if ( results && results.data && results.data.data ) return results.data.data
|
||||||
|
}
|
||||||
|
|
||||||
listing_definition = {
|
listing_definition = {
|
||||||
display: `
|
display: `
|
||||||
LDAP Clients are special user accounts that external applications can use to bind to ${session.get('app.name')}'s built-in LDAP server to allow these applications to authenticate users.
|
LDAP Clients are special user accounts that external applications can use to bind to ${session.get('app.name')}'s built-in LDAP server to allow these applications to authenticate users.
|
||||||
|
@ -32,6 +32,22 @@ class Session {
|
|||||||
if ( permissions.length === 1 ) return result.data.data[permissions[0]]
|
if ( permissions.length === 1 ) return result.data.data[permissions[0]]
|
||||||
return result.data.data
|
return result.data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
url(path) {
|
||||||
|
if ( !path.startsWith('/') ) path = `/${path}`
|
||||||
|
|
||||||
|
let url = this.get('app.url')
|
||||||
|
if ( url.endsWith('/') ) url = url.slice(0, -1)
|
||||||
|
|
||||||
|
return `${url}${path}`
|
||||||
|
}
|
||||||
|
|
||||||
|
host() {
|
||||||
|
let url = this.get('app.url')
|
||||||
|
if ( url.startsWith('http://') ) url = url.substr(7)
|
||||||
|
else if ( url.startsWith('https://') ) url = url.substr(8)
|
||||||
|
return url.split('/')[0].split(':')[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = new Session()
|
const session = new Session()
|
||||||
|
@ -3,7 +3,21 @@ const zxcvbn = require('zxcvbn')
|
|||||||
|
|
||||||
class LDAPController extends Controller {
|
class LDAPController extends Controller {
|
||||||
static get services() {
|
static get services() {
|
||||||
return [...super.services, 'models', 'utility']
|
return [...super.services, 'models', 'utility', 'configs']
|
||||||
|
}
|
||||||
|
|
||||||
|
async get_config(req, res, next) {
|
||||||
|
// ldap port
|
||||||
|
// user base dn
|
||||||
|
// group base dn
|
||||||
|
const config = this.configs.get('ldap:server')
|
||||||
|
return res.api({
|
||||||
|
port: config.port,
|
||||||
|
base_dc: config.schema.base_dc,
|
||||||
|
authentication_base: config.schema.authentication_base,
|
||||||
|
group_base: config.schema.group_base,
|
||||||
|
login_field: config.schema.auth.user_id,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async get_clients(req, res, next) {
|
async get_clients(req, res, next) {
|
||||||
|
16
app/controllers/dash/Misc.controller.js
Normal file
16
app/controllers/dash/Misc.controller.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
const { Controller } = require('libflitter')
|
||||||
|
|
||||||
|
class MiscController extends Controller {
|
||||||
|
static get services() {
|
||||||
|
return [...super.services, 'Vue']
|
||||||
|
}
|
||||||
|
|
||||||
|
get_app_setup(req, res, next) {
|
||||||
|
return res.page('dash:app_setup', {
|
||||||
|
...this.Vue.session(req),
|
||||||
|
...this.Vue.data()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = exports = MiscController
|
@ -22,6 +22,10 @@ const ldap_routes = {
|
|||||||
['middleware::api:Permission', { check: 'v1:ldap:groups:get' }],
|
['middleware::api:Permission', { check: 'v1:ldap:groups:get' }],
|
||||||
'controller::api:v1:LDAP.get_group',
|
'controller::api:v1:LDAP.get_group',
|
||||||
],
|
],
|
||||||
|
'/config': [
|
||||||
|
['middleware::api:Permission', { check: 'v1:ldap:config:get' }],
|
||||||
|
'controller::api:v1:LDAP.get_config',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
post: {
|
post: {
|
||||||
|
@ -5,8 +5,6 @@ const saml_routes = {
|
|||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
// TODO SLO
|
|
||||||
|
|
||||||
get: {
|
get: {
|
||||||
'/metadata.xml': ['controller::saml:SAML.get_metadata'],
|
'/metadata.xml': ['controller::saml:SAML.get_metadata'],
|
||||||
'/sso': [
|
'/sso': [
|
||||||
|
13
app/routing/routers/dash/misc.routes.js
Normal file
13
app/routing/routers/dash/misc.routes.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const misc_routes = {
|
||||||
|
prefix: '/dash',
|
||||||
|
|
||||||
|
middleware: ['auth:UserOnly'],
|
||||||
|
|
||||||
|
get: {
|
||||||
|
'/app/setup': [
|
||||||
|
'controller::dash:Misc.get_app_setup'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = exports = misc_routes
|
7
app/views/dash/app_setup.pug
Normal file
7
app/views/dash/app_setup.pug
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
extends ../theme/dash/base
|
||||||
|
|
||||||
|
block content
|
||||||
|
.cobalt-container
|
||||||
|
.row.pad-top
|
||||||
|
.col-12
|
||||||
|
coreid-app-setup
|
Loading…
Reference in New Issue
Block a user