Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
1d5c00768c
|
|||
|
7f1c9ec9a8
|
|||
|
fe0a4d5991
|
|||
|
f06ff83dce
|
|||
|
251aa6cf97
|
|||
|
60003d64d5
|
|||
|
535dde13ff
|
|||
|
63d102296f
|
|||
|
77d203b2b0
|
|||
|
fcbf25e3ce
|
|||
|
084ec7bbc1
|
|||
|
6b3339a883
|
|||
|
8f1bbfef56
|
|||
|
e400e16ccc
|
|||
|
97096f619f
|
|||
|
2d97b77bbf
|
|||
|
5916222f7b
|
|||
|
bb79d52911
|
|||
|
2e05ec77c8
|
|||
|
433af8261f
|
|||
|
59d831c61f
|
|||
|
fac3431375
|
|||
|
7c8a05aa4f
|
|||
|
1cd306157a
|
|||
|
5eb0487c77
|
|||
|
3f2680671b
|
|||
|
fd06e17d7d
|
|||
|
efdea10b14
|
|||
|
ce7349565e
|
|||
|
a4695a7ecd
|
|||
|
fcb5b58b11
|
@@ -24,6 +24,9 @@ steps:
|
||||
from_secret: deploy_ssh_port
|
||||
script:
|
||||
- cd /home/coreid/CoreID
|
||||
- git checkout master
|
||||
- git pull
|
||||
- git checkout ${DRONE_TAG}
|
||||
- git pull
|
||||
- yarn install
|
||||
when:
|
||||
|
||||
@@ -38,14 +38,18 @@ export default class MFAChallengePage extends Component {
|
||||
static get props() { return ['app_name'] }
|
||||
static get template() { return template }
|
||||
|
||||
loading = false
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
verify_code = ''
|
||||
verify_success = false
|
||||
this.loading = false
|
||||
|
||||
error_message = ''
|
||||
other_message = ''
|
||||
t = {}
|
||||
this.verify_code = ''
|
||||
this.verify_success = false
|
||||
|
||||
this.error_message = ''
|
||||
this.other_message = ''
|
||||
this.t = {}
|
||||
}
|
||||
|
||||
async vue_on_create() {
|
||||
this.t = await T(
|
||||
|
||||
@@ -28,12 +28,16 @@ export default class MFADisableComponent extends Component {
|
||||
static get template() { return template }
|
||||
static get props() { return [] }
|
||||
|
||||
app_name = ''
|
||||
step = 0
|
||||
loading = false
|
||||
error_message = ''
|
||||
other_message = ''
|
||||
t = {}
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.app_name = ''
|
||||
this.step = 0
|
||||
this.loading = false
|
||||
this.error_message = ''
|
||||
this.other_message = ''
|
||||
this.t = {}
|
||||
}
|
||||
|
||||
async vue_on_create() {
|
||||
this.app_name = session.get('app.name')
|
||||
|
||||
@@ -38,12 +38,16 @@ export default class MFARecoveryComponent extends Component {
|
||||
static get template() { return template }
|
||||
static get props() { return ['app_name'] }
|
||||
|
||||
verify_success = false
|
||||
loading = false
|
||||
recovery_code = ''
|
||||
error_message = ''
|
||||
other_message = ''
|
||||
t = {}
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.verify_success = false
|
||||
this.loading = false
|
||||
this.recovery_code = ''
|
||||
this.error_message = ''
|
||||
this.other_message = ''
|
||||
this.t = {}
|
||||
}
|
||||
|
||||
async vue_on_create() {
|
||||
this.t = await T(
|
||||
|
||||
@@ -61,19 +61,23 @@ export default class MFASetupPage extends Component {
|
||||
static get props() { return ['app_name'] }
|
||||
static get template() { return template }
|
||||
|
||||
loading = false
|
||||
step = 0
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
qr_data = ''
|
||||
otpauth_url = ''
|
||||
secret = ''
|
||||
verify_code = ''
|
||||
this.loading = false
|
||||
this.step = 0
|
||||
|
||||
verify_success = false
|
||||
this.qr_data = ''
|
||||
this.otpauth_url = ''
|
||||
this.secret = ''
|
||||
this.verify_code = ''
|
||||
|
||||
error_message = ''
|
||||
other_message = ''
|
||||
t = {}
|
||||
this.verify_success = false
|
||||
|
||||
this.error_message = ''
|
||||
this.other_message = ''
|
||||
this.t = {}
|
||||
}
|
||||
|
||||
async vue_on_create() {
|
||||
this.t = await T(
|
||||
|
||||
@@ -25,7 +25,11 @@ export default class AuthPage extends Component {
|
||||
static get props() { return ['app_name', 'message', 'actions'] }
|
||||
static get template() { return template }
|
||||
|
||||
loading = false
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
async action_click(index) {
|
||||
this.loading = true
|
||||
|
||||
@@ -78,23 +78,27 @@ export default class PasswordResetComponent extends Component {
|
||||
static get template() { return template }
|
||||
static get props() { return ['app_name'] }
|
||||
|
||||
step = 0
|
||||
loading = false
|
||||
has_mfa = false
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
error_message = ''
|
||||
other_message = ''
|
||||
this.step = 0
|
||||
this.loading = false
|
||||
this.has_mfa = false
|
||||
|
||||
step_1_valid = false
|
||||
step_1_calc_time = ''
|
||||
step_1_problem = ''
|
||||
this.error_message = ''
|
||||
this.other_message = ''
|
||||
|
||||
step_2_valid = false
|
||||
this.step_1_valid = false
|
||||
this.step_1_calc_time = ''
|
||||
this.step_1_problem = ''
|
||||
|
||||
password = ''
|
||||
confirm_password = ''
|
||||
t = {}
|
||||
ready = false
|
||||
this.step_2_valid = false
|
||||
|
||||
this.password = ''
|
||||
this.confirm_password = ''
|
||||
this.t = {}
|
||||
this.ready = false
|
||||
}
|
||||
|
||||
async vue_on_create() {
|
||||
this.has_mfa = !!session.get('user.has_mfa')
|
||||
|
||||
@@ -63,18 +63,21 @@ export default class AuthLoginForm extends Component {
|
||||
] }
|
||||
static get template() { return template }
|
||||
|
||||
username = ''
|
||||
password = ''
|
||||
button_text = ''
|
||||
step_two = false
|
||||
btn_disabled = true
|
||||
loading = false
|
||||
error_message = ''
|
||||
other_message = ''
|
||||
allow_back = true
|
||||
auth_user = false
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
t = {}
|
||||
this.username = ''
|
||||
this.password = ''
|
||||
this.button_text = ''
|
||||
this.step_two = false
|
||||
this.btn_disabled = true
|
||||
this.loading = false
|
||||
this.error_message = ''
|
||||
this.other_message = ''
|
||||
this.allow_back = true
|
||||
this.auth_user = false
|
||||
this.t = {}
|
||||
}
|
||||
|
||||
watch_username(new_username, old_username) {
|
||||
this.btn_disabled = !new_username
|
||||
|
||||
@@ -98,19 +98,23 @@ export default class RegistrationFormComponent extends Component {
|
||||
static get template() { return template }
|
||||
static get props() { return ['app_name'] }
|
||||
|
||||
loading = false
|
||||
step = 1
|
||||
other_message = ''
|
||||
error_message = ''
|
||||
message = ''
|
||||
btn_disabled = true
|
||||
button_text = ''
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
first_name = ''
|
||||
last_name = ''
|
||||
username = ''
|
||||
email = ''
|
||||
t = {}
|
||||
this.loading = false
|
||||
this.step = 1
|
||||
this.other_message = ''
|
||||
this.error_message = ''
|
||||
this.message = ''
|
||||
this.btn_disabled = true
|
||||
this.button_text = ''
|
||||
|
||||
this.first_name = ''
|
||||
this.last_name = ''
|
||||
this.username = ''
|
||||
this.email = ''
|
||||
this.t = {}
|
||||
}
|
||||
|
||||
async vue_on_create() {
|
||||
// Batch-load translated phrases
|
||||
|
||||
@@ -146,20 +146,24 @@ export default class FormComponent extends Component {
|
||||
return ['resource', 'form_id', 'initial_mode']
|
||||
}
|
||||
|
||||
definition = {}
|
||||
data = {}
|
||||
uuid = ''
|
||||
title = ''
|
||||
error_message = ''
|
||||
other_message = ''
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
access_msg = ''
|
||||
can_access = false
|
||||
this.definition = {}
|
||||
this.data = {}
|
||||
this.uuid = ''
|
||||
this.title = ''
|
||||
this.error_message = ''
|
||||
this.other_message = ''
|
||||
|
||||
is_ready = false
|
||||
mode = ''
|
||||
id = ''
|
||||
t = {}
|
||||
this.access_msg = ''
|
||||
this.can_access = false
|
||||
|
||||
this.is_ready = false
|
||||
this.mode = ''
|
||||
this.id = ''
|
||||
this.t = {}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.definition = {}
|
||||
|
||||
@@ -65,13 +65,17 @@ export default class ListingComponent extends Component {
|
||||
static get template() { return template }
|
||||
static get props() { return ['resource'] }
|
||||
|
||||
definition = {}
|
||||
data = []
|
||||
resource_class = {}
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
access_msg = ''
|
||||
can_access = false
|
||||
t = {}
|
||||
this.definition = {}
|
||||
this.data = []
|
||||
this.resource_class = {}
|
||||
|
||||
this.access_msg = ''
|
||||
this.can_access = false
|
||||
this.t = {}
|
||||
}
|
||||
|
||||
async vue_on_create() {
|
||||
this.t = await T(
|
||||
|
||||
@@ -232,35 +232,39 @@ export default class AppSetupComponent extends Component {
|
||||
static get template() { return template }
|
||||
static get props() { return [] }
|
||||
|
||||
step = 0
|
||||
btn_disabled = true
|
||||
btn_back = false
|
||||
btn_hidden = false
|
||||
btn_listing = false
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
name = ''
|
||||
identifier = ''
|
||||
type = '' // ldap | saml | oauth
|
||||
oauth_redirect_uri = ''
|
||||
this.step = 0
|
||||
this.btn_disabled = true
|
||||
this.btn_back = false
|
||||
this.btn_hidden = false
|
||||
this.btn_listing = false
|
||||
|
||||
saml_entity_id = ''
|
||||
saml_acs_url = ''
|
||||
saml_slo_url = ''
|
||||
this.name = ''
|
||||
this.identifier = ''
|
||||
this.type = '' // ldap | saml | oauth
|
||||
this.oauth_redirect_uri = ''
|
||||
|
||||
ldap_username = ''
|
||||
ldap_password = ''
|
||||
ldap_password_confirm = ''
|
||||
ldap_config = {}
|
||||
this.saml_entity_id = ''
|
||||
this.saml_acs_url = ''
|
||||
this.saml_slo_url = ''
|
||||
|
||||
error_message = ''
|
||||
this.ldap_username = ''
|
||||
this.ldap_password = ''
|
||||
this.ldap_password_confirm = ''
|
||||
this.ldap_config = {}
|
||||
|
||||
app = {}
|
||||
oauth_client = {}
|
||||
saml_provider = {}
|
||||
ldap_client = {}
|
||||
this.error_message = ''
|
||||
|
||||
app_name = ''
|
||||
host = ''
|
||||
this.app = {}
|
||||
this.oauth_client = {}
|
||||
this.saml_provider = {}
|
||||
this.ldap_client = {}
|
||||
|
||||
this.app_name = ''
|
||||
this.host = ''
|
||||
}
|
||||
|
||||
make_url(path) {
|
||||
return session.url(path)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component } from '../../lib/vues6/vues6.js'
|
||||
import { event_bus } from '../service/EventBus.service.js'
|
||||
import { session } from '../service/Session.service.js'
|
||||
import { message_service } from '../service/Message.service.js'
|
||||
|
||||
const template = `
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light border-bottom">
|
||||
@@ -53,10 +52,10 @@ export default class NavBarComponent extends Component {
|
||||
static get template() { return template }
|
||||
static get props() { return [] }
|
||||
|
||||
can = {}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.can = {}
|
||||
this.toggle_event = event_bus.event('sidebar.toggle')
|
||||
this.first_name = session.get('user.first_name')
|
||||
this.last_name = session.get('user.last_name')
|
||||
|
||||
@@ -23,9 +23,14 @@ export default class SideBarComponent extends Component {
|
||||
static get props() { return ['app_name'] }
|
||||
static get template() { return template }
|
||||
|
||||
actions = []
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
possible_actions = [
|
||||
this.actions = []
|
||||
|
||||
this.isCollapsed = false
|
||||
|
||||
this.possible_actions = [
|
||||
{
|
||||
text: 'Profile',
|
||||
action: 'redirect',
|
||||
@@ -87,8 +92,6 @@ export default class SideBarComponent extends Component {
|
||||
},
|
||||
]
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
event_bus.event('sidebar.toggle').subscribe(() => {
|
||||
this.toggle()
|
||||
})
|
||||
@@ -120,8 +123,6 @@ export default class SideBarComponent extends Component {
|
||||
this.actions = new_actions
|
||||
}
|
||||
|
||||
isCollapsed = false
|
||||
|
||||
toggle() {
|
||||
this.isCollapsed = !this.isCollapsed
|
||||
}
|
||||
|
||||
@@ -68,8 +68,12 @@ export default class MessageContainerComponent extends Component {
|
||||
static get template() { return template }
|
||||
static get props() { return [] }
|
||||
|
||||
messages = []
|
||||
modals = []
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.messages = []
|
||||
this.modals = []
|
||||
}
|
||||
|
||||
vue_on_create() {
|
||||
this.alert_event = event_bus.event('message.alert')
|
||||
|
||||
@@ -195,31 +195,35 @@ export default class EditProfileComponent extends Component {
|
||||
static get template() { return template }
|
||||
static get props() { return ['user_id'] }
|
||||
|
||||
profile_first = ''
|
||||
profile_last = ''
|
||||
profile_email = ''
|
||||
profile_tagline = ''
|
||||
last_reset = ''
|
||||
mfa_enable_date = ''
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
has_mfa_recovery = false
|
||||
mfa_recovery_date = ''
|
||||
mfa_recovery_codes = 0
|
||||
this.profile_first = ''
|
||||
this.profile_last = ''
|
||||
this.profile_email = ''
|
||||
this.profile_tagline = ''
|
||||
this.last_reset = ''
|
||||
this.mfa_enable_date = ''
|
||||
|
||||
form_message = 'No changes.'
|
||||
this.has_mfa_recovery = false
|
||||
this.mfa_recovery_date = ''
|
||||
this.mfa_recovery_codes = 0
|
||||
|
||||
has_mfa = false
|
||||
ready = false
|
||||
this.form_message = 'No changes.'
|
||||
|
||||
notify_gateway_url = ''
|
||||
notify_app_key = ''
|
||||
notify_enabled = false
|
||||
notify_created_on = ''
|
||||
notify_loaded = false
|
||||
this.has_mfa = false
|
||||
this.ready = false
|
||||
|
||||
app_passwords = []
|
||||
app_name = ''
|
||||
t = {}
|
||||
this.notify_gateway_url = ''
|
||||
this.notify_app_key = ''
|
||||
this.notify_enabled = false
|
||||
this.notify_created_on = ''
|
||||
this.notify_loaded = false
|
||||
|
||||
this.app_passwords = []
|
||||
this.app_name = ''
|
||||
this.t = {}
|
||||
}
|
||||
|
||||
on_key_up = ($event) => {}
|
||||
|
||||
|
||||
@@ -72,12 +72,16 @@ export default class AppPasswordFormComponent extends Component {
|
||||
static get template() { return template }
|
||||
static get props() { return [] }
|
||||
|
||||
name = ''
|
||||
valid = false
|
||||
uuid = ''
|
||||
enable_form = true
|
||||
display_password = ''
|
||||
t = {}
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.name = ''
|
||||
this.valid = false
|
||||
this.uuid = ''
|
||||
this.enable_form = true
|
||||
this.display_password = ''
|
||||
this.t = {}
|
||||
}
|
||||
|
||||
async vue_on_create() {
|
||||
this.t = await T(
|
||||
|
||||
@@ -29,8 +29,12 @@ export default class ProfilePhotoUploaderComponent extends Component {
|
||||
static get template() { return template }
|
||||
static get params() { return [] }
|
||||
|
||||
ready = false
|
||||
t = {}
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.ready = false
|
||||
this.t = {}
|
||||
}
|
||||
|
||||
async vue_on_create() {
|
||||
this.t = await T(
|
||||
|
||||
@@ -2,14 +2,17 @@ import CRUDBase from './CRUDBase.js'
|
||||
import { session } from '../service/Session.service.js'
|
||||
|
||||
class AppResource extends CRUDBase {
|
||||
endpoint = '/api/v1/applications'
|
||||
required_fields = ['name', 'identifier']
|
||||
permission_base = 'v1:applications'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'Application'
|
||||
plural = 'Applications'
|
||||
this.endpoint = '/api/v1/applications'
|
||||
this.required_fields = ['name', 'identifier']
|
||||
this.permission_base = 'v1:applications'
|
||||
|
||||
listing_definition = {
|
||||
this.item = 'Application'
|
||||
this.plural = 'Applications'
|
||||
|
||||
this.listing_definition = {
|
||||
display: `
|
||||
An application is anything that can authenticate users against ${session.get('app.name')}. Applications can have any number of associated LDAP clients, SAML service providers, and OAuth2 clients.
|
||||
`,
|
||||
@@ -60,7 +63,7 @@ class AppResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
fields: [
|
||||
{
|
||||
name: 'Name',
|
||||
@@ -123,6 +126,7 @@ class AppResource extends CRUDBase {
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const app = new AppResource()
|
||||
|
||||
@@ -2,15 +2,17 @@ import APIParseError from './APIParseError.js'
|
||||
import { session } from '../service/Session.service.js'
|
||||
|
||||
export default class CRUDBase {
|
||||
endpoint = '/api/v1'
|
||||
required_fields = []
|
||||
permission_base = ''
|
||||
constructor() {
|
||||
this.endpoint = '/api/v1'
|
||||
this.required_fields = []
|
||||
this.permission_base = ''
|
||||
|
||||
listing_definition = {}
|
||||
form_definition = {}
|
||||
this.listing_definition = {}
|
||||
this.form_definition = {}
|
||||
|
||||
item = ''
|
||||
plural = ''
|
||||
this.item = ''
|
||||
this.plural = ''
|
||||
}
|
||||
|
||||
async can(action) {
|
||||
return session.check_permissions(`${this.permission_base}:${action}`)
|
||||
|
||||
@@ -2,14 +2,17 @@ import CRUDBase from './CRUDBase.js'
|
||||
import { session } from '../service/Session.service.js'
|
||||
|
||||
class SettingResource extends CRUDBase {
|
||||
endpoint = '/api/v1/settings'
|
||||
required_fields = ['key', 'value']
|
||||
permission_base = 'v1:settings'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'Setting'
|
||||
plural = 'Settings'
|
||||
this.endpoint = '/api/v1/settings'
|
||||
this.required_fields = ['key', 'value']
|
||||
this.permission_base = 'v1:settings'
|
||||
|
||||
listing_definition = {
|
||||
this.item = 'Setting'
|
||||
this.plural = 'Settings'
|
||||
|
||||
this.listing_definition = {
|
||||
display: `
|
||||
<p>These are advanced settings that allow you to tweak the way ${session.get('app.name')} behaves. Tweak them at your own risk.</p>
|
||||
`,
|
||||
@@ -35,7 +38,7 @@ class SettingResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
fields: [
|
||||
{
|
||||
name: 'Setting Key',
|
||||
@@ -50,6 +53,7 @@ class SettingResource extends CRUDBase {
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const setting = new SettingResource()
|
||||
|
||||
@@ -2,14 +2,17 @@ import CRUDBase from '../CRUDBase.js'
|
||||
import { session } from '../../service/Session.service.js'
|
||||
|
||||
class GroupResource extends CRUDBase {
|
||||
endpoint = '/api/v1/auth/groups'
|
||||
required_fields = ['name']
|
||||
permission_base = 'v1:auth:groups'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'Group'
|
||||
plural = 'Groups'
|
||||
this.endpoint = '/api/v1/auth/groups'
|
||||
this.required_fields = ['name']
|
||||
this.permission_base = 'v1:auth:groups'
|
||||
|
||||
listing_definition = {
|
||||
this.item = 'Group'
|
||||
this.plural = 'Groups'
|
||||
|
||||
this.listing_definition = {
|
||||
display: `
|
||||
In ${session.get('app.name')}, groups are simply a tool for organizing users and assigning permissions and access in bulk. After creating and assigning users to a group, you can manage permissions for that group, and its policies will be applied to all users in that group.
|
||||
`,
|
||||
@@ -50,7 +53,7 @@ class GroupResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
fields: [
|
||||
{
|
||||
name: 'Name',
|
||||
@@ -71,6 +74,7 @@ class GroupResource extends CRUDBase {
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auth_group = new GroupResource()
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import CRUDBase from '../CRUDBase.js'
|
||||
|
||||
class RoleResource extends CRUDBase {
|
||||
endpoint = '/api/v1/auth/roles'
|
||||
required_fields = ['role', 'permissions']
|
||||
permission_base = 'v1:auth:roles'
|
||||
|
||||
item = 'Role'
|
||||
plural = 'Roles'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.endpoint = '/api/v1/auth/roles'
|
||||
this.required_fields = ['role', 'permissions']
|
||||
this.permission_base = 'v1:auth:roles'
|
||||
|
||||
this.item = 'Role'
|
||||
this.plural = 'Roles'
|
||||
}
|
||||
}
|
||||
|
||||
const auth_role = new RoleResource()
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import CRUDBase from '../CRUDBase.js'
|
||||
|
||||
class TrapResource extends CRUDBase {
|
||||
endpoint = '/api/v1/auth/traps'
|
||||
required_fields = ['name', 'trap', 'redirect_to']
|
||||
permission_base = 'v1:auth:traps'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'Trap'
|
||||
plural = 'Traps'
|
||||
this.endpoint = '/api/v1/auth/traps'
|
||||
this.required_fields = ['name', 'trap', 'redirect_to']
|
||||
this.permission_base = 'v1:auth:traps'
|
||||
|
||||
this.item = 'Trap'
|
||||
this.plural = 'Traps'
|
||||
}
|
||||
}
|
||||
|
||||
const auth_trap = new TrapResource()
|
||||
|
||||
@@ -2,14 +2,17 @@ import CRUDBase from '../CRUDBase.js'
|
||||
import { session } from '../../service/Session.service.js'
|
||||
|
||||
class UserResource extends CRUDBase {
|
||||
endpoint = '/api/v1/auth/users'
|
||||
required_fields = ['uid', 'first_name', 'last_name', 'email']
|
||||
permission_base = 'v1:auth:users'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'User'
|
||||
plural = 'Users'
|
||||
this.endpoint = '/api/v1/auth/users'
|
||||
this.required_fields = ['uid', 'first_name', 'last_name', 'email']
|
||||
this.permission_base = 'v1:auth:users'
|
||||
|
||||
listing_definition = {
|
||||
this.item = 'User'
|
||||
this.plural = 'Users'
|
||||
|
||||
this.listing_definition = {
|
||||
display: `
|
||||
Users can be assigned permissions and, if granted, can manage their ${session.get('app.name')} accounts from the Profile page, as well as login to the external applications they've been given access to.
|
||||
`,
|
||||
@@ -57,7 +60,7 @@ class UserResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
fields: [
|
||||
{
|
||||
name: 'First Name',
|
||||
@@ -111,6 +114,7 @@ class UserResource extends CRUDBase {
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auth_user = new UserResource()
|
||||
|
||||
@@ -2,14 +2,17 @@ import CRUDBase from '../CRUDBase.js'
|
||||
import { session } from '../../service/Session.service.js'
|
||||
|
||||
class PolicyResource extends CRUDBase {
|
||||
endpoint = '/api/v1/iam/policy'
|
||||
required_fields = ['entity_id', 'entity_type', 'target_id', 'target_type', 'access_type']
|
||||
permission_base = 'v1:iam:policy'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'IAM Policy'
|
||||
plural = 'IAM Policies'
|
||||
this.endpoint = '/api/v1/iam/policy'
|
||||
this.required_fields = ['entity_id', 'entity_type', 'target_id', 'target_type', 'access_type']
|
||||
this.permission_base = 'v1:iam:policy'
|
||||
|
||||
listing_definition = {
|
||||
this.item = 'IAM Policy'
|
||||
this.plural = 'IAM Policies'
|
||||
|
||||
this.listing_definition = {
|
||||
display: `
|
||||
Identity & Access Management (IAM) policies give you fine grained control over which ${session.get('app.name')} users and groups are allowed to access which applications.
|
||||
<br><br>
|
||||
@@ -65,7 +68,7 @@ class PolicyResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
fields: [
|
||||
{
|
||||
name: 'Subject Type',
|
||||
@@ -73,8 +76,8 @@ class PolicyResource extends CRUDBase {
|
||||
required: true,
|
||||
type: 'select',
|
||||
options: [
|
||||
{ display: 'User', value: 'user' },
|
||||
{ display: 'Group', value: 'group' },
|
||||
{display: 'User', value: 'user'},
|
||||
{display: 'Group', value: 'group'},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -107,8 +110,8 @@ class PolicyResource extends CRUDBase {
|
||||
required: true,
|
||||
type: 'select',
|
||||
options: [
|
||||
{ display: '...is granted access to...', value: 'allow' },
|
||||
{ display: '...is denied access to...', value: 'deny' },
|
||||
{display: '...is granted access to...', value: 'allow'},
|
||||
{display: '...is denied access to...', value: 'deny'},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -117,8 +120,8 @@ class PolicyResource extends CRUDBase {
|
||||
required: true,
|
||||
type: 'select',
|
||||
options: [
|
||||
{ display: 'Application', value: 'application' },
|
||||
{ display: 'API Scope', value: 'api_scope' },
|
||||
{display: 'Application', value: 'application'},
|
||||
{display: 'API Scope', value: 'api_scope'},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -155,6 +158,7 @@ class PolicyResource extends CRUDBase {
|
||||
},
|
||||
},*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const iam_policy = new PolicyResource()
|
||||
|
||||
@@ -2,19 +2,18 @@ import CRUDBase from '../CRUDBase.js'
|
||||
import { session } from '../../service/Session.service.js'
|
||||
|
||||
class ClientResource extends CRUDBase {
|
||||
endpoint = '/api/v1/ldap/clients'
|
||||
required_fields = ['name', 'uid', 'password']
|
||||
permission_base = 'v1:ldap:clients'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'LDAP Client'
|
||||
plural = 'LDAP Clients'
|
||||
this.endpoint = '/api/v1/ldap/clients'
|
||||
this.required_fields = ['name', 'uid', 'password']
|
||||
this.permission_base = 'v1: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
|
||||
}
|
||||
this.item = 'LDAP Client'
|
||||
this.plural = 'LDAP Clients'
|
||||
|
||||
listing_definition = {
|
||||
|
||||
this.listing_definition = {
|
||||
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.
|
||||
<br><br>
|
||||
@@ -56,7 +55,7 @@ class ClientResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
fields: [
|
||||
{
|
||||
name: 'Provider Name',
|
||||
@@ -80,6 +79,12 @@ class ClientResource extends CRUDBase {
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
async server_config() {
|
||||
const results = await axios.get('/api/v1/ldap/config')
|
||||
if (results && results.data && results.data.data) return results.data.data
|
||||
}
|
||||
}
|
||||
|
||||
const ldap_client = new ClientResource()
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import CRUDBase from '../CRUDBase.js'
|
||||
|
||||
class GroupResource extends CRUDBase {
|
||||
endpoint = '/api/v1/ldap/groups'
|
||||
required_fields = ['name', 'role']
|
||||
permission_base = 'v1:ldap:groups'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'LDAP Group'
|
||||
plural = 'LDAP Groups'
|
||||
this.endpoint = '/api/v1/ldap/groups'
|
||||
this.required_fields = ['name', 'role']
|
||||
this.permission_base = 'v1:ldap:groups'
|
||||
|
||||
listing_definition = {
|
||||
this.item = 'LDAP Group'
|
||||
this.plural = 'LDAP Groups'
|
||||
|
||||
this.listing_definition = {
|
||||
columns: [
|
||||
{
|
||||
name: 'Group Name',
|
||||
@@ -50,7 +53,7 @@ class GroupResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
// back_action: {
|
||||
// text: 'Back',
|
||||
// action: 'back',
|
||||
@@ -93,6 +96,7 @@ class GroupResource extends CRUDBase {
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ldap_group = new GroupResource()
|
||||
|
||||
@@ -2,14 +2,17 @@ import CRUDBase from '../CRUDBase.js'
|
||||
import { session } from '../../service/Session.service.js';
|
||||
|
||||
class ClientResource extends CRUDBase {
|
||||
endpoint = '/api/v1/oauth/clients'
|
||||
required_fields = ['name', 'redirect_url', 'api_scopes']
|
||||
permission_base = 'v1:oauth:clients'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'OAuth2 Client'
|
||||
plural = 'OAuth2 Clients'
|
||||
this.endpoint = '/api/v1/oauth/clients'
|
||||
this.required_fields = ['name', 'redirect_url', 'api_scopes']
|
||||
this.permission_base = 'v1:oauth:clients'
|
||||
|
||||
listing_definition = {
|
||||
this.item = 'OAuth2 Client'
|
||||
this.plural = 'OAuth2 Clients'
|
||||
|
||||
this.listing_definition = {
|
||||
display: `
|
||||
OAuth2 clients are applications that support authentication over the OAuth2 protocol. This allows you to add a "Sign-In with XXX" button for ${session.get('app.name')} to the application in question. To do this, you need to create an OAuth2 client for that application, and provide the name, redirect URL, and API scopes.
|
||||
<br><br>
|
||||
@@ -58,7 +61,7 @@ class ClientResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
fields: [
|
||||
{
|
||||
name: 'Client Name',
|
||||
@@ -101,6 +104,7 @@ class ClientResource extends CRUDBase {
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const oauth_client = new ClientResource()
|
||||
|
||||
@@ -2,14 +2,17 @@ import CRUDBase from '../CRUDBase.js'
|
||||
import { session } from '../../service/Session.service.js'
|
||||
|
||||
class ClientResource extends CRUDBase {
|
||||
endpoint = '/openid/clients'
|
||||
required_fields = ['client_name', 'grant_types', 'redirect_uri']
|
||||
permission_base = 'v1:openid:clients'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'OpenID Connect Client'
|
||||
plural = 'OpenID Connect Clients'
|
||||
this.endpoint = '/openid/clients'
|
||||
this.required_fields = ['client_name', 'grant_types', 'redirect_uri']
|
||||
this.permission_base = 'v1:openid:clients'
|
||||
|
||||
listing_definition = {
|
||||
this.item = 'OpenID Connect Client'
|
||||
this.plural = 'OpenID Connect Clients'
|
||||
|
||||
this.listing_definition = {
|
||||
display: `
|
||||
OpenID Connect clients are applications that support authentication over the OpenID Connect protocol. This allows you to add a "Sign-In with XXX" button for ${session.get('app.name')} to the application in question. To do this, the application need only comply with the OpenID standards.
|
||||
`,
|
||||
@@ -49,7 +52,7 @@ class ClientResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
fields: [
|
||||
{
|
||||
name: 'Client Name',
|
||||
@@ -70,8 +73,8 @@ class ClientResource extends CRUDBase {
|
||||
field: 'grant_types',
|
||||
type: 'select.multiple',
|
||||
options: [
|
||||
{ display: 'Refresh Token', value: 'refresh_token' },
|
||||
{ display: 'Authorization Code', value: 'authorization_code' },
|
||||
{display: 'Refresh Token', value: 'refresh_token'},
|
||||
{display: 'Authorization Code', value: 'authorization_code'},
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
@@ -91,6 +94,7 @@ class ClientResource extends CRUDBase {
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const openid_client = new ClientResource()
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import CRUDBase from '../CRUDBase.js'
|
||||
|
||||
class ScopeResource extends CRUDBase {
|
||||
endpoint = '/api/v1/reflect/scopes'
|
||||
required_fields = ['scope']
|
||||
permission_base = 'v1:reflect:scopes'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'API Scope'
|
||||
plural = 'API Scopes'
|
||||
this.endpoint = '/api/v1/reflect/scopes'
|
||||
this.required_fields = ['scope']
|
||||
this.permission_base = 'v1:reflect:scopes'
|
||||
|
||||
this.item = 'API Scope'
|
||||
this.plural = 'API Scopes'
|
||||
}
|
||||
}
|
||||
|
||||
const reflect_scope = new ScopeResource()
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import CRUDBase from '../CRUDBase.js'
|
||||
|
||||
class TokenResource extends CRUDBase {
|
||||
endpoint = '/api/v1/reflect/tokens'
|
||||
required_fields = ['client_id']
|
||||
permission_base = 'v1:reflect:tokens'
|
||||
constructor() {
|
||||
super()
|
||||
this.endpoint = '/api/v1/reflect/tokens'
|
||||
this.required_fields = ['client_id']
|
||||
this.permission_base = 'v1:reflect:tokens'
|
||||
|
||||
item = 'API Token'
|
||||
plural = 'API Tokens'
|
||||
this.item = 'API Token'
|
||||
this.plural = 'API Tokens'
|
||||
|
||||
listing_definition = {
|
||||
this.listing_definition = {
|
||||
display: `
|
||||
This allows you to create bearer tokens manually to allow for easier testing of the API. Notably, this is meant as a measure for testing and development, not for long term use.
|
||||
<br><br>
|
||||
@@ -54,7 +56,7 @@ class TokenResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
fields: [
|
||||
{
|
||||
name: 'Client',
|
||||
@@ -83,6 +85,7 @@ class TokenResource extends CRUDBase {
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const reflect_token = new TokenResource()
|
||||
|
||||
@@ -2,14 +2,17 @@ import CRUDBase from '../CRUDBase.js'
|
||||
import { session } from '../../service/Session.service.js'
|
||||
|
||||
class ProviderResource extends CRUDBase {
|
||||
endpoint = '/api/v1/saml/providers'
|
||||
required_fields = ['name', 'acs_url', 'entity_id']
|
||||
permission_base = 'v1:saml:providers'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'SAML Service Provider'
|
||||
plural = 'SAML Service Providers'
|
||||
this.endpoint = '/api/v1/saml/providers'
|
||||
this.required_fields = ['name', 'acs_url', 'entity_id']
|
||||
this.permission_base = 'v1:saml:providers'
|
||||
|
||||
listing_definition = {
|
||||
this.item = 'SAML Service Provider'
|
||||
this.plural = 'SAML Service Providers'
|
||||
|
||||
this.listing_definition = {
|
||||
display: `SAML Service Providers are applications that support external authentication to a SAML Identity Provider. In this case, ${session.get('app.name')} is the identity provider, so these external applications can authenticate against it.
|
||||
<br><br>
|
||||
To do this, you need to know the SAML service provider's entity ID, assertion consumer service URL, and single-logout URL (if supported).`,
|
||||
@@ -58,7 +61,7 @@ class ProviderResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
fields: [
|
||||
{
|
||||
name: 'Provider Name',
|
||||
@@ -89,6 +92,7 @@ class ProviderResource extends CRUDBase {
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const saml_provider = new ProviderResource()
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import CRUDBase from '../CRUDBase.js'
|
||||
|
||||
class AnnouncementResource extends CRUDBase {
|
||||
endpoint = '/api/v1/system/announcements'
|
||||
required_fields = ['user_ids', 'group_ids', 'title', 'message', 'type']
|
||||
permission_base = 'v1:system:announcements'
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
item = 'System Announcement'
|
||||
plural = 'System Announcements'
|
||||
this.endpoint = '/api/v1/system/announcements'
|
||||
this.required_fields = ['user_ids', 'group_ids', 'title', 'message', 'type']
|
||||
this.permission_base = 'v1:system:announcements'
|
||||
|
||||
listing_definition = {
|
||||
this.item = 'System Announcement'
|
||||
this.plural = 'System Announcements'
|
||||
|
||||
this.listing_definition = {
|
||||
display: `
|
||||
System announcements are administrative messages that you want all or some of your users to see. These messages can be delivered via e-mail, as a message after login, or as a system banner announcement.
|
||||
`,
|
||||
@@ -42,7 +45,7 @@ class AnnouncementResource extends CRUDBase {
|
||||
],
|
||||
}
|
||||
|
||||
form_definition = {
|
||||
this.form_definition = {
|
||||
fields: [
|
||||
{
|
||||
name: 'Title',
|
||||
@@ -79,9 +82,9 @@ class AnnouncementResource extends CRUDBase {
|
||||
field: 'type',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ display: 'Login Intercept', value: 'login' },
|
||||
{ display: 'E-Mail', value: 'email' },
|
||||
{ display: 'System Banner', value: 'banner' },
|
||||
{display: 'Login Intercept', value: 'login'},
|
||||
{display: 'E-Mail', value: 'email'},
|
||||
{display: 'System Banner', value: 'banner'},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -92,6 +95,7 @@ class AnnouncementResource extends CRUDBase {
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const system_announcement = new AnnouncementResource()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
class Event {
|
||||
firings = []
|
||||
subscriptions = []
|
||||
|
||||
constructor(name) {
|
||||
this.name = name
|
||||
this.firings = []
|
||||
this.subscriptions = []
|
||||
}
|
||||
|
||||
subscribe(handler) {
|
||||
@@ -22,7 +22,9 @@ class Event {
|
||||
}
|
||||
|
||||
class EventBusService {
|
||||
_events = {}
|
||||
constructor() {
|
||||
this._events = {}
|
||||
}
|
||||
|
||||
event(name) {
|
||||
if ( !this._events[name] ) {
|
||||
|
||||
@@ -2,7 +2,9 @@ import { event_bus } from './EventBus.service.js'
|
||||
import { auth_api } from './AuthApi.service.js'
|
||||
|
||||
class MessageService {
|
||||
listener_interval = 25000
|
||||
constructor() {
|
||||
this.listener_interval = 25000
|
||||
}
|
||||
|
||||
alert({type, message, timeout = 0, on_dismiss = () => {} }) {
|
||||
event_bus.event('message.alert').fire({ type, message, timeout, on_dismiss })
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class Session {
|
||||
data = {}
|
||||
constructor() {
|
||||
this.data = {}
|
||||
}
|
||||
|
||||
init(data) {
|
||||
this.data = data
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class TranslateService {
|
||||
_cache = {}
|
||||
constructor() {
|
||||
this._cache = {}
|
||||
}
|
||||
|
||||
check_cache(...keys) {
|
||||
const obj = {}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class UtilityService {
|
||||
_debounce_timeouts = {}
|
||||
constructor() {
|
||||
this._debounce_timeouts = {}
|
||||
}
|
||||
|
||||
uuid() {
|
||||
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
|
||||
|
||||
35
app/assets/error-log.js
Normal file
35
app/assets/error-log.js
Normal file
@@ -0,0 +1,35 @@
|
||||
window.COREID_ERROR_LOG_URL = window.COREID_ERROR_LOG_URL || '/api/v1/log-error'
|
||||
|
||||
async function logError(error) {
|
||||
try {
|
||||
await fetch(window.COREID_ERROR_LOG_URL, {
|
||||
method: 'POST',
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
full_url: window.location.href,
|
||||
trace: [
|
||||
error.name + ': ' + error.message,
|
||||
error.stack,
|
||||
].join('\n')
|
||||
}),
|
||||
})
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
;(function() {
|
||||
var old_onerror = window.onerror
|
||||
|
||||
window.onerror = function(msg, src, line, col, error) {
|
||||
logError(error).then(function() {
|
||||
if ( typeof old_onerror === 'function' ) {
|
||||
try {
|
||||
old_onerror(msg, src, line, col, error)
|
||||
} catch(e) {}
|
||||
}
|
||||
})
|
||||
}
|
||||
})()
|
||||
1
app/assets/lib/axios/axios.min.js
vendored
1
app/assets/lib/axios/axios.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/assets/lib/popper/popper-1.16.0.min.js
vendored
1
app/assets/lib/popper/popper-1.16.0.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -18,6 +18,11 @@ class CoreIDAdapter {
|
||||
expiresAt = new Date(Date.now() + (expiresIn * 1000))
|
||||
}
|
||||
|
||||
if ( payload.uid ) {
|
||||
payload.originalUid = payload.uid
|
||||
payload.uid = payload.uid.toLowerCase()
|
||||
}
|
||||
|
||||
await this.coll().updateOne(
|
||||
{ _id },
|
||||
{ $set: { payload, ...(expiresAt ? { expiresAt } : undefined) } },
|
||||
@@ -34,6 +39,11 @@ class CoreIDAdapter {
|
||||
).limit(1).next()
|
||||
|
||||
if (!result) return undefined
|
||||
|
||||
if ( result?.payload?.originalUid ) {
|
||||
result.payload.uid = result.payload.originalUid
|
||||
}
|
||||
|
||||
return result.payload
|
||||
}
|
||||
|
||||
@@ -49,11 +59,16 @@ class CoreIDAdapter {
|
||||
|
||||
async findByUid(uid) {
|
||||
const result = await this.coll().find(
|
||||
{ 'payload.uid': uid },
|
||||
{ 'payload.uid': uid.toLowerCase() },
|
||||
{ payload: 1 },
|
||||
).limit(1).next()
|
||||
|
||||
if (!result) return undefined
|
||||
|
||||
if ( result?.payload?.originalUid ) {
|
||||
result.payload.uid = result.payload.originalUid
|
||||
}
|
||||
|
||||
return result.payload
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class FlitterProfileMapper {
|
||||
getClaims() {
|
||||
const claims = {}
|
||||
|
||||
claims[this.map.nameIdentifier] = this.user.uid
|
||||
claims[this.map.nameIdentifier] = this.user.uid.toLowerCase()
|
||||
claims[this.map.email] = this.user.email
|
||||
claims[this.map.name] = `${this.user.first_name} ${this.user.last_name}`
|
||||
claims[this.map.givenname] = this.user.first_name
|
||||
@@ -54,7 +54,7 @@ class FlitterProfileMapper {
|
||||
}
|
||||
|
||||
getNameIdentifier() {
|
||||
return { nameIdentifier: this.user.uid }
|
||||
return { nameIdentifier: this.user.uid.toLowerCase() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,12 @@ class Home extends Controller {
|
||||
async tmpl(req, res) {
|
||||
return res.page('tmpl', {...this.Vue.data(), ...this.Vue.session(req)})
|
||||
}
|
||||
|
||||
async log_front_end_error(req, res, next) {
|
||||
const FrontEndError = this.models.get('FrontEndError')
|
||||
await FrontEndError.log(req)
|
||||
return res.api()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Home
|
||||
|
||||
@@ -119,14 +119,12 @@ class OpenIDController extends Controller {
|
||||
uid, prompt, params, session,
|
||||
} = await this.openid_connect.provider.interactionDetails(req, res)
|
||||
|
||||
console.log({uid, prompt, params, session})
|
||||
|
||||
const name = prompt.name
|
||||
if ( typeof this[name] !== 'function' ) {
|
||||
return this.fail(res, 'Sorry, something has gone wrong.')
|
||||
}
|
||||
|
||||
return this[name](req, res, { uid, prompt, params, session })
|
||||
return this[name](req, res, { uid: uid.toLowerCase(), prompt, params, session })
|
||||
}
|
||||
|
||||
async consent(req, res, { uid, prompt, params, session }) {
|
||||
@@ -142,13 +140,13 @@ class OpenIDController extends Controller {
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
const application = await Application.findOne({ openid_client_ids: params.client_id })
|
||||
if ( !application ) {
|
||||
this.output.warning('IAM Denial!')
|
||||
this.output.warn('IAM Denial!')
|
||||
return this.Vue.auth_message(res, {
|
||||
message: req.T('saml.no_access').replace('APP_NAME', 'this application'),
|
||||
next_destination: '/dash',
|
||||
})
|
||||
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
||||
this.output.warning('IAM Denial!')
|
||||
this.output.warn('IAM Denial!')
|
||||
return this.Vue.auth_message(res, {
|
||||
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||
next_destination: '/dash',
|
||||
@@ -172,7 +170,7 @@ class OpenIDController extends Controller {
|
||||
{
|
||||
text: req.T('common.grant'),
|
||||
action: 'redirect',
|
||||
next: `/openid/interaction/${uid}/grant`,
|
||||
next: `/openid/interaction/${uid.toLowerCase()}/grant`,
|
||||
},
|
||||
],
|
||||
})
|
||||
@@ -180,7 +178,7 @@ class OpenIDController extends Controller {
|
||||
}
|
||||
|
||||
async login(req, res, { uid, prompt, params, session }) {
|
||||
return res.redirect(`/openid/interaction/${uid}/start-session`)
|
||||
return res.redirect(`/openid/interaction/${uid.toLowerCase()}/start-session`)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,13 +200,13 @@ class OpenIDController extends Controller {
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
const application = await Application.findOne({ openid_client_ids: params.client_id })
|
||||
if ( !application ) {
|
||||
this.output.warning('IAM Denial!')
|
||||
this.output.warn('IAM Denial!')
|
||||
return this.Vue.auth_message(res, {
|
||||
message: req.T('saml.no_access').replace('APP_NAME', 'this application'),
|
||||
next_destination: '/dash',
|
||||
})
|
||||
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
||||
this.output.warning('IAM Denial!')
|
||||
this.output.warn('IAM Denial!')
|
||||
return this.Vue.auth_message(res, {
|
||||
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||
next_destination: '/dash',
|
||||
@@ -238,13 +236,13 @@ class OpenIDController extends Controller {
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
const application = await Application.findOne({ openid_client_ids: params.client_id })
|
||||
if ( !application ) {
|
||||
this.output.warning('IAM Denial!')
|
||||
this.output.warn('IAM Denial!')
|
||||
return this.Vue.auth_message(res, {
|
||||
message: req.T('saml.no_access').replace('APP_NAME', 'this application'),
|
||||
next_destination: '/dash',
|
||||
})
|
||||
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
||||
this.output.warning('IAM Denial!')
|
||||
this.output.warn('IAM Denial!')
|
||||
return this.Vue.auth_message(res, {
|
||||
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||
next_destination: '/dash',
|
||||
|
||||
@@ -71,7 +71,7 @@ class AuthController extends Controller {
|
||||
const user = new User({
|
||||
first_name: req.body.first_name,
|
||||
last_name: req.body.last_name,
|
||||
uid: req.body.uid,
|
||||
uid: req.body.uid.toLowerCase(),
|
||||
email: req.body.email,
|
||||
trap: 'password_reset', // Force user to reset password
|
||||
})
|
||||
@@ -297,7 +297,7 @@ class AuthController extends Controller {
|
||||
.api()
|
||||
|
||||
const user = new User({
|
||||
uid: req.body.uid,
|
||||
uid: req.body.uid.toLowerCase(),
|
||||
email: req.body.email,
|
||||
first_name: req.body.first_name,
|
||||
last_name: req.body.last_name,
|
||||
@@ -417,7 +417,7 @@ class AuthController extends Controller {
|
||||
|
||||
user.first_name = req.body.first_name
|
||||
user.last_name = req.body.last_name
|
||||
user.uid = req.body.uid
|
||||
user.uid = req.body.uid.toLowerCase()
|
||||
user.email = req.body.email
|
||||
|
||||
if ( req.body.tagline )
|
||||
@@ -493,7 +493,7 @@ class AuthController extends Controller {
|
||||
|
||||
if ( is_valid ) {
|
||||
const User = this.models.get('auth:User')
|
||||
const user = await User.findOne({uid: req.body.username})
|
||||
const user = await User.findOne({uid: req.body.username.toLowerCase()})
|
||||
if ( !user || !user.can_login ) is_valid = false
|
||||
}
|
||||
|
||||
@@ -511,7 +511,7 @@ class AuthController extends Controller {
|
||||
const data = {}
|
||||
if ( req.body.username ) {
|
||||
const existing_user = await User.findOne({
|
||||
uid: req.body.username,
|
||||
uid: req.body.username.toLowerCase(),
|
||||
})
|
||||
|
||||
data.username_taken = !!existing_user
|
||||
@@ -544,7 +544,8 @@ class AuthController extends Controller {
|
||||
.message(req.T('auth.unable_to_complete'))
|
||||
.api({ errors })
|
||||
|
||||
const login_args = await flitter.get_login_args(req.body)
|
||||
const [username, ...other_args] = await flitter.get_login_args(req.body)
|
||||
const login_args = [username.toLowerCase(), ...other_args]
|
||||
const user = await flitter.login.apply(flitter, login_args)
|
||||
|
||||
if ( !user )
|
||||
|
||||
@@ -96,7 +96,7 @@ class LDAPController extends Controller {
|
||||
|
||||
// Make sure the uid is free
|
||||
const User = this.models.get('auth:User')
|
||||
const existing_user = await User.findOne({ uid: req.body.uid })
|
||||
const existing_user = await User.findOne({ uid: req.body.uid.toLowerCase() })
|
||||
if ( existing_user )
|
||||
return res.status(400)
|
||||
.message(req.T('api.user_already_exists'))
|
||||
@@ -113,7 +113,7 @@ class LDAPController extends Controller {
|
||||
// Create the client
|
||||
const Client = this.models.get('ldap:Client')
|
||||
const client = await Client.create({
|
||||
uid: req.body.uid,
|
||||
uid: req.body.uid.toLowerCase(),
|
||||
password: req.body.password,
|
||||
name: req.body.name,
|
||||
})
|
||||
@@ -210,16 +210,16 @@ class LDAPController extends Controller {
|
||||
}
|
||||
|
||||
// Update the uid
|
||||
if ( req.body.uid !== user.uid ) {
|
||||
if ( req.body.uid.toLowerCase() !== user.uid ) {
|
||||
// Make sure the UID is free
|
||||
const User = this.models.get('auth:User')
|
||||
const existing_user = await User.findOne({ uid: req.body.uid })
|
||||
const existing_user = await User.findOne({ uid: req.body.uid.toLowerCase() })
|
||||
if ( existing_user )
|
||||
return res.status(400)
|
||||
.message(req.T('api.user_already_exists'))
|
||||
.api()
|
||||
|
||||
user.uid = req.body.uid
|
||||
user.uid = req.body.uid.toLowerCase()
|
||||
}
|
||||
|
||||
// Update the password
|
||||
|
||||
@@ -8,7 +8,7 @@ const Oauth2Controller = require('flitter-auth/controllers/Oauth2')
|
||||
*/
|
||||
class Oauth2 extends Oauth2Controller {
|
||||
static get services() {
|
||||
return [...super.services, 'Vue', 'configs', 'models']
|
||||
return [...super.services, 'Vue', 'configs', 'models', 'output']
|
||||
}
|
||||
|
||||
async authorize_post(req, res, next) {
|
||||
@@ -18,6 +18,24 @@ class Oauth2 extends Oauth2Controller {
|
||||
const StarshipClient = this.models.get('oauth:Client')
|
||||
const starship_client = await StarshipClient.findOne({ active: true, uuid: client.clientID })
|
||||
|
||||
// Make sure the user has IAM access before proceeding
|
||||
const Application = this.models.get('Application')
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
const application = await Application.findOne({ oauth_client_ids: starship_client.id })
|
||||
if ( !application ) {
|
||||
this.output.warn('IAM Denial!')
|
||||
return this.Vue.auth_message(res, {
|
||||
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||
next_destination: '/dash',
|
||||
})
|
||||
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
||||
this.output.warn('IAM Denial!')
|
||||
return this.Vue.auth_message(res, {
|
||||
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||
next_destination: '/dash',
|
||||
})
|
||||
}
|
||||
|
||||
req.user.authorize(starship_client)
|
||||
await req.user.save()
|
||||
return super.authorize_post(req, res, next)
|
||||
@@ -31,6 +49,24 @@ class Oauth2 extends Oauth2Controller {
|
||||
const StarshipClient = this.models.get('oauth:Client')
|
||||
const starship_client = await StarshipClient.findOne({ active: true, uuid: client.clientID })
|
||||
|
||||
// Make sure the user has IAM access before proceeding
|
||||
const Application = this.models.get('Application')
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
const application = await Application.findOne({ oauth_client_ids: starship_client.id })
|
||||
if ( !application ) {
|
||||
this.output.warn('IAM Denial!')
|
||||
return this.Vue.auth_message(res, {
|
||||
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||
next_destination: '/dash',
|
||||
})
|
||||
} else if ( !(await Policy.check_user_access(req.user, application.id)) ) {
|
||||
this.output.warn('IAM Denial!')
|
||||
return this.Vue.auth_message(res, {
|
||||
message: req.T('saml.no_access').replace('APP_NAME', application.name),
|
||||
next_destination: '/dash',
|
||||
})
|
||||
}
|
||||
|
||||
if ( req.user.has_authorized(starship_client) ) {
|
||||
return this.Vue.invoke_action(res, {
|
||||
text: 'Grant Access',
|
||||
|
||||
@@ -67,7 +67,7 @@ class SAMLController extends Controller {
|
||||
key: await this.saml.private_key(),
|
||||
protocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
|
||||
clearIdPSession: done => {
|
||||
this.output.info(`${req.T('saml.clear_idp_session')} ${req.user.uid}`)
|
||||
this.output.info(`${req.T('saml.clear_idp_session')} ${req.user.uid.toLowerCase()}`)
|
||||
req.saml.participants.clear().then(async () => {
|
||||
if ( this.saml.config().slo.end_coreid_session ) {
|
||||
await req.user.logout(req)
|
||||
|
||||
@@ -50,7 +50,7 @@ class LDAPController extends Injectable {
|
||||
const item = await this.get_resource_from_dn(req.dn)
|
||||
if ( !item ) {
|
||||
this.output.debug(`Bind failure: ${req.dn} not found`)
|
||||
return next(new LDAP.NoSuchObject())
|
||||
return next(new LDAP.NoSuchObjectError())
|
||||
}
|
||||
|
||||
// If the object is can-able, make sure it can bind
|
||||
|
||||
@@ -52,7 +52,7 @@ class UsersController extends LDAPController {
|
||||
first_name: req_data.cn ? req_data.cn[0] : '',
|
||||
last_name: req_data.sn ? req_data.sn[0] : '',
|
||||
email: req_data.mail ? req_data.mail[0] : '',
|
||||
username: req_data.uid ? req_data.uid[0] : '',
|
||||
username: req_data.uid ? req_data.uid[0].toLowerCase() : '',
|
||||
password: req_data.userpassword ? req_data.userpassword[0] : '',
|
||||
}
|
||||
|
||||
@@ -218,10 +218,12 @@ class UsersController extends LDAPController {
|
||||
// TODO flitter-orm chunk query
|
||||
// TODO generalize scoped search logic
|
||||
async search_people(req, res, next) {
|
||||
if ( !req.user.can('ldap:search:users') ) {
|
||||
if ( !req.user.can('ldap:search:users:me') ) {
|
||||
return next(new LDAP.InsufficientAccessRightsError())
|
||||
}
|
||||
|
||||
const can_search_all = req.user.can('ldap:search:users')
|
||||
|
||||
const iam_targets = this.parse_iam_targets(req.filter)
|
||||
if ( req.scope === 'base' ) {
|
||||
// If scope is base, check if the base DN matches the filter.
|
||||
@@ -231,7 +233,12 @@ class UsersController extends LDAPController {
|
||||
const user = await this.get_resource_from_dn(req.dn)
|
||||
|
||||
// Make sure the user is ldap visible && match the filter
|
||||
if ( user && user.ldap_visible && req.filter.matches(await user.to_ldap(iam_targets)) ) {
|
||||
if (
|
||||
user
|
||||
&& user.ldap_visible
|
||||
&& req.filter.matches(await user.to_ldap(iam_targets))
|
||||
&& (req.user.id === user.id || can_search_all)
|
||||
) {
|
||||
|
||||
// If so, send the object
|
||||
res.send({
|
||||
@@ -255,6 +262,7 @@ class UsersController extends LDAPController {
|
||||
// Fetch the LDAP-visible users
|
||||
const users = await this.User.ldap_directory()
|
||||
for ( const user of users ) {
|
||||
if ( user.id !== req.user.id && !can_search_all ) continue
|
||||
|
||||
// Make sure the user os of the appropriate scope
|
||||
if ( req.dn.equals(user.dn) || user.dn.parent().equals(req.dn) ) {
|
||||
@@ -283,6 +291,7 @@ class UsersController extends LDAPController {
|
||||
this.output.debug(`Filter:`)
|
||||
this.output.debug(this.filter_to_obj(req.filter.json))
|
||||
for ( const user of users ) {
|
||||
if ( user.id !== req.user.id && !can_search_all ) continue
|
||||
this.output.debug(`Checking ${user.uid}...`)
|
||||
this.output.debug(`DN: ${user.dn}`)
|
||||
this.output.debug(`Req DN equals: ${req.dn.equals(user.dn)}`)
|
||||
@@ -290,6 +299,7 @@ class UsersController extends LDAPController {
|
||||
|
||||
// Make sure the user is of appropriate scope
|
||||
if ( req.dn.equals(user.dn) || req.dn.parentOf(user.dn) ) {
|
||||
this.output.debug(await user.to_ldap())
|
||||
this.output.debug(`Matches sub scope. Matches filter? ${req.filter.matches(await user.to_ldap(iam_targets))}`)
|
||||
|
||||
// Check if filter matches
|
||||
@@ -317,7 +327,7 @@ class UsersController extends LDAPController {
|
||||
|
||||
try {
|
||||
if ( typeof dn === 'string' ) dn = LDAP.parseDN(dn)
|
||||
return dn.rdns[0].attrs[uid_field].value
|
||||
return dn.rdns[0].attrs[uid_field].value.toLowerCase()
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@@ -325,7 +335,7 @@ class UsersController extends LDAPController {
|
||||
const uid = this.get_uid_from_dn(dn)
|
||||
if ( uid ) {
|
||||
const User = this.models.get('auth:User')
|
||||
return User.findOne({uid, ldap_visible: true})
|
||||
return User.findOne({uid: uid.toLowerCase(), ldap_visible: true})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
app/models/FrontEndError.model.js
Normal file
29
app/models/FrontEndError.model.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const { Model } = require('flitter-orm')
|
||||
|
||||
class FrontEndErrorModel extends Model {
|
||||
static get schema() {
|
||||
return {
|
||||
user_agent: String,
|
||||
logged_at: { type: Date, default: () => new Date },
|
||||
user_id: String,
|
||||
session_id: String,
|
||||
full_url: String,
|
||||
trace: String,
|
||||
}
|
||||
}
|
||||
|
||||
static async log(request) {
|
||||
const err = new this({
|
||||
user_agent: request.get('user-agent'),
|
||||
user_id: request?.user?.id,
|
||||
session_id: request.sessionID,
|
||||
full_url: request.body.full_url,
|
||||
trace: request.body.trace,
|
||||
})
|
||||
|
||||
await err.save()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = FrontEndErrorModel
|
||||
@@ -173,7 +173,7 @@ class User extends AuthUser {
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
|
||||
const ldap_data = {
|
||||
uid: this.uid,
|
||||
uid: this.uid.toLowerCase(),
|
||||
uuid: this.uuid,
|
||||
cn: this.first_name,
|
||||
sn: this.last_name,
|
||||
@@ -213,7 +213,7 @@ class User extends AuthUser {
|
||||
}
|
||||
|
||||
get dn() {
|
||||
return LDAP.parseDN(`uid=${this.uid},${this.ldap_server.auth_dn().format(this.configs.get('ldap:server.format'))}`)
|
||||
return LDAP.parseDN(`uid=${this.uid.toLowerCase()},${this.ldap_server.auth_dn().format(this.configs.get('ldap:server.format'))}`)
|
||||
}
|
||||
|
||||
// The following are used by OpenID connect
|
||||
@@ -227,15 +227,15 @@ class User extends AuthUser {
|
||||
given_name: this.first_name,
|
||||
locale: 'en_US', // TODO
|
||||
name: `${this.first_name} ${this.last_name}`,
|
||||
preferred_username: this.uid,
|
||||
username: this.uid,
|
||||
preferred_username: this.uid.toLowerCase(),
|
||||
username: this.uid.toLowerCase(),
|
||||
}
|
||||
}
|
||||
|
||||
static async findByLogin(login) {
|
||||
return this.findOne({
|
||||
active: true,
|
||||
uid: login,
|
||||
uid: login.toLowerCase(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ class PolicyModel extends Model {
|
||||
if ( this.entity_type === 'user' ) {
|
||||
const User = this.models.get('auth:User')
|
||||
const user = await User.findById(this.entity_id)
|
||||
entity_display = `User: ${user.last_name}, ${user.first_name} (${user.uid})`
|
||||
entity_display = `User: ${user.last_name}, ${user.first_name} (${user.uid.toLowerCase()})`
|
||||
} else if ( this.entity_type === 'group' ) {
|
||||
const Group = this.models.get('auth:Group')
|
||||
const group = await Group.findById(this.entity_id)
|
||||
|
||||
@@ -19,7 +19,7 @@ class ClientModel extends Model {
|
||||
const user = new User({
|
||||
first_name: name,
|
||||
last_name: '(LDAP Agent)',
|
||||
uid,
|
||||
uid: uid.toLowerCase(),
|
||||
roles: ['ldap_client'],
|
||||
})
|
||||
|
||||
@@ -58,7 +58,7 @@ class ClientModel extends Model {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
user_id: user.id,
|
||||
uid: user.uid,
|
||||
uid: user.uid.toLowerCase(),
|
||||
last_invocation: this.last_invocation,
|
||||
permissions: [...user.permissions, ...role_permissions],
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class SessionParticipantStore extends Injectable {
|
||||
async issue({ service_provider }) {
|
||||
const sp = new this.SessionParticipant({
|
||||
service_provider_id: service_provider.id,
|
||||
name_id: this.request.user.uid,
|
||||
name_id: this.request.user.uid.toLowerCase(),
|
||||
// session_index: this.get_index(),
|
||||
slo_url: service_provider.slo_url,
|
||||
// TODO sp_cert,
|
||||
|
||||
@@ -8,22 +8,35 @@ class PermissionMiddleware extends Middleware {
|
||||
async test(req, res, next, { check }) {
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
|
||||
if ( !req.additional_api_log_data ) req.additional_api_log_data = {}
|
||||
req.additional_api_log_data.permission_check = check
|
||||
|
||||
// If the request was authorized using an OAuth2 bearer token,
|
||||
// make sure the associated client has permission to access this endpoint.
|
||||
if ( req?.oauth?.client ) {
|
||||
if ( !req.oauth.client.can(check) ) {
|
||||
const reason = 'oauth-permission-fail'
|
||||
await this.activity.api_access_denial({
|
||||
const fail_activity = await this.activity.api_access_denial({
|
||||
req,
|
||||
reason,
|
||||
check,
|
||||
oauth_client_id: req.oauth.client.id,
|
||||
oauth_client_id: req.oauth.client.uuid,
|
||||
})
|
||||
|
||||
req.additional_api_log_data.permission_check_succeeded = false
|
||||
req.additional_api_log_data.permission_check_activity_id = fail_activity.id
|
||||
|
||||
return res.status(401)
|
||||
.message('Insufficient permissions (OAuth2 Client).')
|
||||
.api()
|
||||
}
|
||||
|
||||
req.additional_api_log_data.permission_check_succeeded = true
|
||||
|
||||
// If the oauth2 client has this permission, then allow the request to continue,
|
||||
// even if the user does not.
|
||||
// OAuth2Clients need to be able to query users via the API.
|
||||
return next()
|
||||
}
|
||||
|
||||
const policy_denied = await Policy.check_user_denied(req.user, check)
|
||||
@@ -33,13 +46,18 @@ class PermissionMiddleware extends Middleware {
|
||||
if ( policy_denied || (!req.user.can(check) && !policy_access) ) {
|
||||
// Record the failed API access
|
||||
const reason = policy_denied ? 'iam-denial' : (!req.user.can(check) ? 'user-permission-fail' : 'iam-not-granted')
|
||||
await this.activity.api_access_denial({ req, reason, check })
|
||||
const fail_activity = await this.activity.api_access_denial({ req, reason, check })
|
||||
|
||||
req.additional_api_log_data.permission_check_succeeded = false
|
||||
req.additional_api_log_data.permission_check_reason = reason
|
||||
req.additional_api_log_data.permission_check_activity_id = fail_activity.id
|
||||
|
||||
return res.status(401)
|
||||
.message('Insufficient permissions.')
|
||||
.api()
|
||||
}
|
||||
|
||||
req.additional_api_log_data.permission_check_succeeded = true
|
||||
return next()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,21 @@ class APIRouteMiddleware extends Middleware {
|
||||
}
|
||||
|
||||
async test(req, res, next, { allow_token = true, allow_user = true }) {
|
||||
if ( !req.additional_api_log_data ) req.additional_api_log_data = {}
|
||||
|
||||
// First, check if there is a user in the session.
|
||||
if ( allow_user && req.is_auth ) {
|
||||
if ( allow_user && req.user ) {
|
||||
req.additional_api_log_data.authorized_by = 'user'
|
||||
return next()
|
||||
} else if ( allow_token ) {
|
||||
if ( !req.oauth ) req.oauth = {}
|
||||
req.additional_api_log_data.attempted_token_auth = true
|
||||
|
||||
return req.app.oauth2.authorise()(req, res, async e => {
|
||||
if ( e ) return next(e)
|
||||
|
||||
req.additional_api_log_data.authorized_by = 'token'
|
||||
|
||||
// Look up the OAuth2 client an inject it into the route
|
||||
if ( req.user && req.user.id ) {
|
||||
const User = this.models.get('auth:User')
|
||||
@@ -42,6 +51,9 @@ class APIRouteMiddleware extends Middleware {
|
||||
.message('This OAuth2 client is no longer authorized.')
|
||||
.api()
|
||||
|
||||
req.additional_api_log_data.token_client_id = client.uuid
|
||||
req.additional_api_log_data.token = bearer
|
||||
|
||||
req.oauth.token = token
|
||||
req.oauth.client = client
|
||||
} else
|
||||
@@ -51,10 +63,10 @@ class APIRouteMiddleware extends Middleware {
|
||||
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
} else {
|
||||
return res.status(401).api()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = APIRouteMiddleware
|
||||
|
||||
@@ -59,6 +59,10 @@ const index = {
|
||||
'middleware::auth:GuestOnly',
|
||||
'controller::api:v1:Password.request_reset',
|
||||
],
|
||||
|
||||
'/api/v1/log-error': [
|
||||
'controller::Home.log_front_end_error'
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ class MFAService extends Service {
|
||||
secret(user) {
|
||||
return speakeasy.generateSecret({
|
||||
length: this.configs.get('auth.mfa.secret_length') ?? 20,
|
||||
name: `${this.configs.get('app.name')} (${user.uid})`,
|
||||
name: `${this.configs.get('app.name')} (${user.uid.toLowerCase()})`,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class VueService extends Service {
|
||||
user: {
|
||||
first_name: req.user.first_name,
|
||||
last_name: req.user.last_name,
|
||||
username: req.user.uid,
|
||||
username: req.user.uid.toLowerCase(),
|
||||
email: req.user.email,
|
||||
tagline: req.user.tagline,
|
||||
user_id: req.user.id,
|
||||
|
||||
@@ -36,6 +36,7 @@ class ActivityService extends Service {
|
||||
}
|
||||
|
||||
await activity.save()
|
||||
return activity
|
||||
}
|
||||
|
||||
async mfa_enable({ req }) {
|
||||
|
||||
@@ -28,7 +28,7 @@ class OpenIDConnectUnit extends Unit {
|
||||
clients: [],
|
||||
interactions: {
|
||||
interactions,
|
||||
url: (ctx, interaction) => `/openid/interaction/${ctx.oidc.uid}`,
|
||||
url: (ctx, interaction) => `/openid/interaction/${ctx.oidc.uid.toLowerCase()}`,
|
||||
},
|
||||
cookies: {
|
||||
long: { signed: true, maxAge: 24 * 60 * 60 * 1000 }, // 1 day, ms
|
||||
|
||||
@@ -10,6 +10,7 @@ class SettingsUnit extends Unit {
|
||||
}
|
||||
|
||||
async go(app) {
|
||||
Error.stackTraceLimit = 50
|
||||
app.express.set('trust proxy', true)
|
||||
|
||||
const Setting = this.models.get('Setting')
|
||||
|
||||
@@ -15,6 +15,7 @@ html(lang='en')
|
||||
.app-container
|
||||
block app
|
||||
block script
|
||||
script(src='/assets/error-log.js')
|
||||
script(src='/assets/lib/axios/axios.min.js')
|
||||
script(src='/assets/lib/jquery/jquery-3.4.1.slim.min.js')
|
||||
script(src='/assets/lib/popper/popper-1.16.0.min.js')
|
||||
|
||||
@@ -194,6 +194,7 @@ const auth_config = {
|
||||
|
||||
// LDAP Binding
|
||||
'ldap:bind',
|
||||
'ldap:search:users:me',
|
||||
],
|
||||
|
||||
root: ['v1', 'ldap', 'saml', 'profile', 'oauth', 'app', 'auth', 'iam'],
|
||||
|
||||
@@ -31,6 +31,13 @@ const jobs_config = {
|
||||
// worker processes of the same type.
|
||||
// (e.g. you can have two "main" workers)
|
||||
},
|
||||
|
||||
connector: {
|
||||
enabled: env('JOB_QUEUE_CONNECTOR', false),
|
||||
mount: env('JOB_QUEUE_CONNECTOR_MOUNT', '/job_queue_api'),
|
||||
secret: env('JOB_QUEUE_CONNECTOR_SECRET'),
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
module.exports = exports = jobs_config
|
||||
|
||||
@@ -23,6 +23,10 @@ const server_config = {
|
||||
level: env("LOGGING_LEVEL", 2),
|
||||
|
||||
include_timestamp: env("LOGGING_TIMESTAMP", false),
|
||||
|
||||
api_logging: env('LOG_API_RESPONSES', false),
|
||||
|
||||
error_logging: env('LOG_REQUEST_ERRORS', true),
|
||||
},
|
||||
|
||||
session: {
|
||||
@@ -31,7 +35,12 @@ const server_config = {
|
||||
* The secret used to encrypt the session.
|
||||
* This should be set in the environment.
|
||||
*/
|
||||
secret: env("SECRET", "changeme")
|
||||
secret: env("SECRET", "changeme"),
|
||||
|
||||
/*
|
||||
* The max age of a session in milliseconds
|
||||
*/
|
||||
max_age: env("SESSION_MAX_AGE", 1000 * 60 * 60 * 24 * 2), // default to 2 days
|
||||
},
|
||||
|
||||
uploads: {
|
||||
|
||||
@@ -33,3 +33,6 @@ SMTP_PORT="587"
|
||||
SMTP_USER="coreid@localhost.localdomain"
|
||||
SMTP_DEFAULT_SENDER="coreid@localhost.localdomain"
|
||||
SMTP_PASS="something super secure"
|
||||
|
||||
JOB_QUEUE_CONNECTOR=true
|
||||
JOB_QUEUE_CONNECTOR_SECRET=
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"flitter-forms": "^0.8.1",
|
||||
"flitter-gotify": "^0.1.0",
|
||||
"flitter-i18n": "^0.1.1",
|
||||
"flitter-jobs": "^0.1.2",
|
||||
"flitter-jobs": "^0.3.0",
|
||||
"flitter-less": "^0.5.3",
|
||||
"flitter-orm": "^0.4.0",
|
||||
"flitter-redis": "^0.1.1",
|
||||
@@ -33,7 +33,7 @@
|
||||
"ioredis": "^4.17.1",
|
||||
"is-absolute-url": "^3.0.3",
|
||||
"ldapjs": "^1.0.2",
|
||||
"libflitter": "^0.53.1",
|
||||
"libflitter": "^0.57.1",
|
||||
"moment": "^2.24.0",
|
||||
"mongodb": "^3.5.9",
|
||||
"nodemailer": "^6.4.6",
|
||||
|
||||
16
yarn.lock
16
yarn.lock
@@ -2148,10 +2148,10 @@ flitter-i18n@^0.1.1:
|
||||
ncp "^2.0.0"
|
||||
pluralize "^8.0.0"
|
||||
|
||||
flitter-jobs@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/flitter-jobs/-/flitter-jobs-0.1.2.tgz#5536bb12be728b61f6e0940b6c18760e4f1b59d6"
|
||||
integrity sha512-bxJD3akDnoKPRyIXXy8ytlypGI8uj0Fjn2C8gIY2HFxZeFIBJo47MqQ9qZvVWFS28pS6aUY+0emOvTFkuG/2zA==
|
||||
flitter-jobs@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/flitter-jobs/-/flitter-jobs-0.3.0.tgz#c10fd45e4ab3c41c32ad4d8540b9b76547c20fbd"
|
||||
integrity sha512-uapjauWZCoZwc+3DuL0W1tY4LozSXTr3FqrKJEcL/Ri8diRYjZuT0+v+kiZBJ4YzThcjjXF+aYoFL09QlIG7Zw==
|
||||
dependencies:
|
||||
bullmq "^1.8.8"
|
||||
flitter-redis "^0.1.1"
|
||||
@@ -3235,10 +3235,10 @@ leven@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/leven/-/leven-1.0.2.tgz#9144b6eebca5f1d0680169f1a6770dcea60b75c3"
|
||||
integrity sha1-kUS27ryl8dBoAWnxpncNzqYLdcM=
|
||||
|
||||
libflitter@^0.53.1:
|
||||
version "0.53.1"
|
||||
resolved "https://registry.yarnpkg.com/libflitter/-/libflitter-0.53.1.tgz#30b1838763a228fba8b9c820d2cad501c3aa0117"
|
||||
integrity sha512-EK3okZyt0pmnpsZNx2lYOIcwgtmSOEPh4a5xE3pXM9RVc3dtXXscgJ5h9OvLTIN9WfRc7T5VTdpOjeAK6Xmysg==
|
||||
libflitter@^0.57.1:
|
||||
version "0.57.1"
|
||||
resolved "https://registry.yarnpkg.com/libflitter/-/libflitter-0.57.1.tgz#e605333a5e38cadac1203e4be8ea685c5520472b"
|
||||
integrity sha512-bEu02HZjcAp53A2xnE0hz/LVhkErvnqaKmlNOqvJJaGMjKClfVuqJlr9bFfWU84qXL9SaHWd06P3nxIVKTvFIw==
|
||||
dependencies:
|
||||
colors "^1.3.3"
|
||||
connect-mongodb-session "^2.2.0"
|
||||
|
||||
Reference in New Issue
Block a user