Improved external user auth config. Fixed browser tests.

This commit is contained in:
Martin Edenhofer 2016-08-15 19:48:35 +02:00
parent f643acfa94
commit 19802f88a2
14 changed files with 1219 additions and 273 deletions

View file

@ -72,7 +72,7 @@ class App.DashboardFirstSteps extends App.Controller
)
$('.modal .modal-body').html(finish)
)
@delay(create, 1800)
@delay(create, 2800)
template
testTicketFinish: (data) ->

View file

@ -8,8 +8,8 @@ class Security extends App.ControllerTabs
@tabs = [
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Security::Base' } }
{ name: 'Password', 'target': 'password', controller: App.SettingsArea, params: { area: 'Security::Password' } }
#{ name: 'Authentication', 'target': 'auth', controller: App.SettingsArea, params: { area: 'Security::Authentication' } }
{ name: 'Third-Party Applications', 'target': 'third_party_auth', controller: App.SettingsThirdPartyAuthentication, params: { area: 'Security::ThirdPartyAuthentication' } }
#{ name: 'Authentication', 'target': 'auth', controller: App.SettingsArea, params: { area: 'Security::Authentication' } }
{ name: 'Third-Party Applications', 'target': 'third_party_auth', controller: App.SettingsArea, params: { area: 'Security::ThirdPartyAuthentication' } }
]
@render()

View file

@ -4,7 +4,11 @@ class App.SettingsArea extends App.Controller
# check authentication
@authenticateCheckRedirect()
@subscribeId = App.Setting.subscribe(@render, initFetch: true, clear: false)
if App.Setting.count() is 0
App.Setting.fetchFull(@render)
return
@render()
#@subscribeId = App.Setting.subscribe(@render, initFetch: true, clear: false)
render: =>
@ -31,179 +35,10 @@ class App.SettingsArea extends App.Controller
elements = []
for setting in settings
if setting.name is 'product_logo'
item = new App.SettingsAreaLogo(setting: setting)
if setting.preferences.controller && App[setting.preferences.controller]
item = new App[setting.preferences.controller](setting: setting)
else
item = new App.SettingsAreaItem(setting: setting)
elements.push item.el
@html elements
class App.SettingsAreaItem extends App.Controller
events:
'submit form': 'update'
constructor: ->
super
@render()
render: =>
# defaults
directValue = 0
for item in @setting.options['form']
directValue += 1
if directValue > 1
for item in @setting.options['form']
item['default'] = @setting.state_current.value[item.name]
else
item['default'] = @setting.state_current.value
# form
@configure_attributes = @setting.options['form']
# item
@html App.view('settings/item')(
setting: @setting
)
new App.ControllerForm(
el: @el.find('.form-item'),
model: { configure_attributes: @configure_attributes, className: '' }
autofocus: false
)
update: (e) =>
e.preventDefault()
@formDisable(e)
params = @formParam(e.target)
directValue = 0
directData = undefined
for item in @setting.options['form']
directValue += 1
directData = params[item.name]
if directValue > 1
state_current = {
value: params
}
#App.Config.set((@setting.name, params)
else
state_current = {
value: directData
}
#App.Config.set(@setting.name, directData)
@setting['state_current'] = state_current
ui = @
@setting.save(
done: =>
ui.formEnable(e)
App.Event.trigger 'notify', {
type: 'success'
msg: App.i18n.translateContent('Update successful!')
timeout: 2000
}
# rerender ui || get new collections and session data
App.Setting.preferencesPost(@setting)
fail: (settings, details) ->
ui.formEnable(e)
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!')
timeout: 2000
}
)
class App.SettingsAreaLogo extends App.Controller
elements:
'.logo-preview': 'logoPreview'
events:
'submit form': 'submit'
'change .js-upload': 'onLogoPick'
constructor: ->
super
@render()
render: ->
localElement = $(App.view('settings/logo')(
setting: @setting
))
localElement.find('.js-loginPreview').html( App.view('generic/login_preview')(
logoUrl: @logoUrl()
logoChange: true
))
@html localElement
onLogoPick: (event) =>
reader = new FileReader()
reader.onload = (e) =>
@logoPreview.attr('src', e.target.result)
file = event.target.files[0]
# if no file is given, about in file upload was used
return if !file
maxSiteInMb = 8
if file.size && file.size > 1024 * 1024 * maxSiteInMb
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent('File too big, max. %s MB allowed.', maxSiteInMb)
timeout: 2000
}
@logoPreview.attr('src', '')
return
reader.readAsDataURL(file)
submit: (e) =>
e.preventDefault()
@formDisable(e)
# get params
@params = @formParam(e.target)
# add logo
@params.logo = @logoPreview.attr('src')
store = (logoResizeDataUrl) =>
# store image
@params.logo_resize = logoResizeDataUrl
@ajax(
id: "setting_image_#{@setting.id}"
type: 'PUT'
url: "#{@apiPath}/settings/image/#{@setting.id}"
data: JSON.stringify(@params)
processData: true
success: (data, status, xhr) =>
@formEnable(e)
if data.result is 'ok'
App.Event.trigger 'notify', {
type: 'success'
msg: App.i18n.translateContent('Update successful!')
timeout: 2000
}
for setting in data.settings
value = App.Setting.get(setting.name)
App.Config.set(name, value)
else
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent(data.message)
timeout: 2000
}
fail: =>
@formEnable(e)
)
# add resized image
App.ImageService.resizeForApp(@params.logo, @logoPreview.width(), @logoPreview.height(), store)

View file

@ -0,0 +1,78 @@
class App.SettingsAreaItem extends App.Controller
events:
'submit form': 'update'
constructor: ->
super
@render()
render: =>
# defaults
directValue = 0
for item in @setting.options['form']
directValue += 1
if directValue > 1
for item in @setting.options['form']
item['default'] = @setting.state_current.value[item.name]
else
item['default'] = @setting.state_current.value
# form
@configure_attributes = @setting.options['form']
# item
@html App.view('settings/item')(
setting: @setting
)
new App.ControllerForm(
el: @el.find('.form-item'),
model: { configure_attributes: @configure_attributes, className: '' }
autofocus: false
)
update: (e) =>
e.preventDefault()
@formDisable(e)
params = @formParam(e.target)
directValue = 0
directData = undefined
for item in @setting.options['form']
directValue += 1
directData = params[item.name]
if directValue > 1
state_current = {
value: params
}
#App.Config.set((@setting.name, params)
else
state_current = {
value: directData
}
#App.Config.set(@setting.name, directData)
@setting['state_current'] = state_current
ui = @
@setting.save(
done: =>
ui.formEnable(e)
App.Event.trigger 'notify', {
type: 'success'
msg: App.i18n.translateContent('Update successful!')
timeout: 2000
}
# rerender ui || get new collections and session data
App.Setting.preferencesPost(@setting)
fail: (settings, details) ->
ui.formEnable(e)
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!')
timeout: 2000
}
)

View file

@ -0,0 +1,89 @@
class App.SettingsAreaLogo extends App.Controller
elements:
'.logo-preview': 'logoPreview'
events:
'submit form': 'submit'
'change .js-upload': 'onLogoPick'
constructor: ->
super
@render()
render: ->
localElement = $(App.view('settings/logo')(
setting: @setting
))
localElement.find('.js-loginPreview').html( App.view('generic/login_preview')(
logoUrl: @logoUrl()
logoChange: true
))
@html localElement
onLogoPick: (event) =>
reader = new FileReader()
reader.onload = (e) =>
@logoPreview.attr('src', e.target.result)
file = event.target.files[0]
# if no file is given, about in file upload was used
return if !file
maxSiteInMb = 8
if file.size && file.size > 1024 * 1024 * maxSiteInMb
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent('File too big, max. %s MB allowed.', maxSiteInMb)
timeout: 2000
}
@logoPreview.attr('src', '')
return
reader.readAsDataURL(file)
submit: (e) =>
e.preventDefault()
@formDisable(e)
# get params
@params = @formParam(e.target)
# add logo
@params.logo = @logoPreview.attr('src')
store = (logoResizeDataUrl) =>
# store image
@params.logo_resize = logoResizeDataUrl
@ajax(
id: "setting_image_#{@setting.id}"
type: 'PUT'
url: "#{@apiPath}/settings/image/#{@setting.id}"
data: JSON.stringify(@params)
processData: true
success: (data, status, xhr) =>
@formEnable(e)
if data.result is 'ok'
App.Event.trigger 'notify', {
type: 'success'
msg: App.i18n.translateContent('Update successful!')
timeout: 2000
}
for setting in data.settings
value = App.Setting.get(setting.name)
App.Config.set(name, value)
else
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent(data.message)
timeout: 2000
}
fail: =>
@formEnable(e)
)
# add resized image
App.ImageService.resizeForApp(@params.logo, @logoPreview.width(), @logoPreview.height(), store)

View file

@ -0,0 +1,97 @@
class App.SettingsAreaSwitch extends App.Controller
events:
'change .js-setting input': 'toggleSetting'
'submit form': 'update'
elements:
'.js-setting input': 'uiSetting'
constructor: ->
super
@render()
render: =>
# defaults
directValue = 0
for item in @setting.options['form']
directValue += 1
if directValue > 1
for item in @setting.options['form']
item['default'] = @setting.state_current.value[item.name]
else
item['default'] = @setting.state_current.value
# form
@configure_attributes = @setting.options['form']
@subSetting = []
for localSetting in @setting.preferences.sub
@subSetting.push App.Setting.findByAttribute('name', localSetting)
# item
@html App.view('settings/switch')(
checked: App.Setting.get(@setting.name)
setting: @setting
subSetting: @subSetting
)
for localSetting in @subSetting
console.log('localSetting', localSetting.state_current)
new App.ControllerForm(
el: @$('.form-item')
params: localSetting.state_current.value
model: { configure_attributes: localSetting.options['form'], className: '' }
autofocus: false
)
toggleSetting: =>
value = @uiSetting.prop('checked')
App.Setting.set(@setting.name, value)
update: (e) =>
e.preventDefault()
@formDisable(e)
params = @formParam(e.target)
localSetting = $(e.currentTarget).data('name')
setting = App.Setting.findByAttribute('name', localSetting)
directValue = 0
directData = undefined
for item in setting.options['form']
directValue += 1
directData = params[item.name]
if directValue > 1
state_current = {
value: params
}
#App.Config.set((setting.name, params)
else
state_current = {
value: directData
}
#App.Config.set(setting.name, directData)
setting['state_current'] = state_current
ui = @
setting.save(
done: ->
ui.formEnable(e)
App.Event.trigger 'notify', {
type: 'success'
msg: App.i18n.translateContent('Update successful!')
timeout: 2000
}
# rerender ui || get new collections and session data
App.Setting.preferencesPost(setting)
fail: (settings, details) ->
ui.formEnable(e)
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!')
timeout: 2000
}
)

View file

@ -23,9 +23,8 @@ class Index extends App.ControllerContent
# observe config changes related to login page
@bind('config_update_local', (data) =>
return if data.name != 'maintenance_mode' &&
data.name != 'maintenance_login' &&
data.name != 'maintenance_login_message' &&
return if !data.name.match(/^maintenance/) &&
!data.name.match(/^auth/) &&
data.name != 'user_lost_password' &&
data.name != 'user_create_account' &&
data.name != 'product_name' &&
@ -38,33 +37,45 @@ class Index extends App.ControllerContent
render: (data = {}) ->
auth_provider_all = {
facebook: {
url: '/auth/facebook',
name: 'Facebook',
config: 'auth_facebook',
class: 'facebook',
url: '/auth/facebook'
name: 'Facebook'
config: 'auth_facebook'
class: 'facebook'
},
twitter: {
url: '/auth/twitter',
name: 'Twitter',
config: 'auth_twitter',
class: 'twitter',
url: '/auth/twitter'
name: 'Twitter'
config: 'auth_twitter'
class: 'twitter'
},
linkedin: {
url: '/auth/linkedin',
name: 'LinkedIn',
config: 'auth_linkedin',
class: 'linkedin',
url: '/auth/linkedin'
name: 'LinkedIn'
config: 'auth_linkedin'
class: 'linkedin'
},
github: {
url: '/auth/github'
name: 'Github'
config: 'auth_github'
class: 'github'
},
gitlab: {
url: '/auth/gitlab'
name: 'Gitlab'
config: 'auth_gitlab'
class: 'gitlab'
},
google_oauth2: {
url: '/auth/google_oauth2',
name: 'Google',
config: 'auth_google_oauth2',
class: 'google',
url: '/auth/google_oauth2'
name: 'Google'
config: 'auth_google_oauth2'
class: 'google'
},
}
auth_providers = []
for key, provider of auth_provider_all
if @Config.get( provider.config ) is true || @Config.get( provider.config ) is 'true'
if @Config.get(provider.config) is true || @Config.get(provider.config) is 'true'
auth_providers.push provider
@html App.view('login')(

View file

@ -218,7 +218,7 @@ class _i18nSingleton extends Spine.Module
if quote
translated = App.Utils.htmlEscape(translated)
# apply inline markup
# apply inline markup pre
if markup
translated = translated
.replace(/\|\|(.+?)\|\|/gm, '<i>$1</i>')
@ -241,6 +241,11 @@ class _i18nSingleton extends Spine.Module
"<a href=\"#{arg}\">🔗</a>"
)
# apply inline markup post
if markup
translated = translated
.replace(/\[(.+?)\]\((.+?)\)/gm, '<a href="$2" target="_blank">$1</a>')
@log 'debug', 'translate', string, args, translated
# return translated string

View file

@ -1,2 +1,4 @@
<p><%- @T('A Test Ticket has been created, you can find it in your overview |"%s"|', @overviewName) %></p>
<p><%- @T('To open and work on it, click on the Ticket |#%s| directly %l or in the overview |"%s"| %l and click on the Ticket.', @ticketNumber, @ticketUrl, @overviewName, @overviewUrl) %></p>
<p><%- @T('A Test Ticket has been created, you can find it in your overview "%s" %l.', @overviewName, @overviewUrl) %></p>
<div class="centered">
<a class="btn btn--success flex" href="<%- @ticketUrl %>"><%- @T('Open Ticket# %s', @ticketNumber) %></a>
</div>

View file

@ -0,0 +1,19 @@
<div class="page-header">
<div class="page-header-title">
<div class="zammad-switch zammad-switch--small js-setting" data-name="<%= @setting.name %>">
<input name="<%= @setting.name %>" type="checkbox" id="setting-<%= @setting.name %>" <% if @checked: %>checked<% end %>>
<label for="setting-<%= @setting.name %>"></label>
</div>
<h1><%- @T.apply(@, [@setting.title].concat(@setting.preferences.title_i18n)) %></h1>
</div>
</div>
<div class="page-content">
<p class="help-text"><%- @T.apply(@, [@setting.description].concat(@setting.preferences.description_i18n)) %></p>
<% for localSetting in @subSetting: %>
<form class="settings-entry" data-name="<%= localSetting.name %>">
<div class="horizontal end">
<div class="form-item flex"></div>
<button type="submit" class="btn btn--primary"><%- @T('Submit') %></button>
</div>
</form>
</div>

View file

@ -0,0 +1,562 @@
class UpdateSettingAuth < ActiveRecord::Migration
def up
# return if it's a new setup
return if !Setting.find_by(name: 'system_init_done')
Role.create_or_update(
id: 1,
name: 'Admin',
note: 'To configure your system.',
preferences: {
not: ['Customer'],
},
default_at_signup: false,
updated_by_id: 1,
created_by_id: 1
)
Role.create_or_update(
id: 2,
name: 'Agent',
note: 'To work on Tickets.',
default_at_signup: false,
preferences: {
not: ['Customer'],
},
updated_by_id: 1,
created_by_id: 1
)
Role.create_or_update(
id: 3,
name: 'Customer',
note: 'People who create Tickets ask for help.',
preferences: {
not: %w(Agent Admin),
},
default_at_signup: true,
updated_by_id: 1,
created_by_id: 1
)
Role.create_or_update(
id: 4,
name: 'Report',
note: 'Access the report area.',
preferences: {
not: ['Customer'],
},
default_at_signup: false,
created_by_id: 1,
updated_by_id: 1,
)
ObjectManager::Attribute.add(
force: true,
object: 'Organization',
name: 'shared',
display: 'Shared organization',
data_type: 'boolean',
data_option: {
null: true,
default: true,
note: 'Customers in the organization can view each other items.',
item_class: 'formGroup--halfSize',
translate: true,
options: {
true: 'yes',
false: 'no',
}
},
editable: false,
active: true,
screens: {
edit: {
Admin: {
null: false,
},
},
view: {
'-all-' => {
shown: true,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 1400,
)
ObjectManager::Attribute.add(
force: true,
object: 'User',
name: 'role_ids',
display: 'Permissions',
data_type: 'user_permission',
data_option: {
null: false,
item_class: 'checkbox',
},
editable: false,
active: true,
screens: {
signup: {},
invite_agent: {
'-all-' => {
null: false,
default: [Role.lookup(name: 'Agent').id],
},
},
invite_customer: {},
edit: {
Admin: {
null: true,
},
},
view: {
'-all-' => {
shown: false,
},
},
},
to_create: false,
to_migrate: false,
to_delete: false,
position: 1600,
)
Setting.create_if_not_exists(
title: 'Authentication via %s',
name: 'auth_ldap',
area: 'Security::Authentication',
description: 'Enables user authentication via %s.',
preferences: {
title_i18n: ['LDAP'],
description_i18n: ['LDAP']
},
state: {
adapter: 'Auth::Ldap',
host: 'localhost',
port: 389,
bind_dn: 'cn=Manager,dc=example,dc=org',
bind_pw: 'example',
uid: 'mail',
base: 'dc=example,dc=org',
always_filter: '',
always_roles: %w(Admin Agent),
always_groups: ['Users'],
sync_params: {
firstname: 'sn',
lastname: 'givenName',
email: 'mail',
login: 'mail',
},
},
frontend: false
)
Setting.create_or_update(
title: 'Authentication via %s',
name: 'auth_twitter',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
display: '',
null: true,
name: 'auth_twitter',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_twitter_credentials'],
title_i18n: ['Twitter'],
description_i18n: ['Twitter', 'Twitter Developer Site', 'https://dev.twitter.com/apps']
},
state: false,
frontend: true
)
Setting.create_or_update(
title: 'Twitter App Credentials',
name: 'auth_twitter_credentials',
area: 'Security::ThirdPartyAuthentication::Twitter',
description: 'App credentials for Twitter.',
options: {
form: [
{
display: 'Twitter Key',
null: true,
name: 'key',
tag: 'input',
},
{
display: 'Twitter Secret',
null: true,
name: 'secret',
tag: 'input',
},
],
},
state: {},
frontend: false
)
Setting.create_or_update(
title: 'Authentication via %s',
name: 'auth_facebook',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
display: '',
null: true,
name: 'auth_facebook',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_facebook_credentials'],
title_i18n: ['Facebook'],
description_i18n: ['Facebook', 'Facebook Developer Site', 'https://developers.facebook.com/apps/']
},
state: false,
frontend: true
)
Setting.create_or_update(
title: 'Facebook App Credentials',
name: 'auth_facebook_credentials',
area: 'Security::ThirdPartyAuthentication::Facebook',
description: 'App credentials for Facebook.',
options: {
form: [
{
display: 'App ID',
null: true,
name: 'app_id',
tag: 'input',
},
{
display: 'App Secret',
null: true,
name: 'app_secret',
tag: 'input',
},
],
},
state: {},
frontend: false
)
Setting.create_or_update(
title: 'Authentication via %s',
name: 'auth_google_oauth2',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
display: '',
null: true,
name: 'auth_google_oauth2',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_google_oauth2_credentials'],
title_i18n: ['Google'],
description_i18n: ['Google', 'Google API Console Site', 'https://console.developers.google.com/apis/credentials']
},
state: false,
frontend: true
)
Setting.create_or_update(
title: 'Google App Credentials',
name: 'auth_google_oauth2_credentials',
area: 'Security::ThirdPartyAuthentication::Google',
description: 'Enables user authentication via Google.',
options: {
form: [
{
display: 'Client ID',
null: true,
name: 'client_id',
tag: 'input',
},
{
display: 'Client Secret',
null: true,
name: 'client_secret',
tag: 'input',
},
],
},
state: {},
frontend: false
)
Setting.create_or_update(
title: 'Authentication via %s',
name: 'auth_linkedin',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
display: '',
null: true,
name: 'auth_linkedin',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_linkedin_credentials'],
title_i18n: ['LinkedIn'],
description_i18n: ['LinkedIn', 'Linkedin Developer Site', 'https://www.linkedin.com/developer/apps']
},
state: false,
frontend: true
)
Setting.create_or_update(
title: 'LinkedIn App Credentials',
name: 'auth_linkedin_credentials',
area: 'Security::ThirdPartyAuthentication::Linkedin',
description: 'Enables user authentication via LinkedIn.',
options: {
form: [
{
display: 'App ID',
null: true,
name: 'app_id',
tag: 'input',
},
{
display: 'App Secret',
null: true,
name: 'app_secret',
tag: 'input',
},
],
},
state: {},
frontend: false
)
Setting.create_or_update(
title: 'Authentication via %s',
name: 'auth_github',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
display: '',
null: true,
name: 'auth_github',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_github_credentials'],
title_i18n: ['Github'],
description_i18n: ['Github', 'Github OAuth Applications', 'https://github.com/settings/applications']
},
state: false,
frontend: true
)
Setting.create_or_update(
title: 'Github App Credentials',
name: 'auth_github_credentials',
area: 'Security::ThirdPartyAuthentication::Github',
description: 'Enables user authentication via Github.',
options: {
form: [
{
display: 'App ID',
null: true,
name: 'app_id',
tag: 'input',
},
{
display: 'App Secret',
null: true,
name: 'app_secret',
tag: 'input',
},
],
},
state: {},
frontend: false
)
Setting.create_or_update(
title: 'Authentication via %s',
name: 'auth_gitlab',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
display: '',
null: true,
name: 'auth_gitlab',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_gitlab_credentials'],
title_i18n: ['Gitlab'],
description_i18n: ['Gitlab', 'Gitlab Applications', 'https://your-gitlab-host/admin/applications']
},
state: false,
frontend: true
)
Setting.create_or_update(
title: 'Gitlab App Credentials',
name: 'auth_gitlab_credentials',
area: 'Security::ThirdPartyAuthentication::Gitlab',
description: 'Enables user authentication via Gitlab.',
options: {
form: [
{
display: 'App ID',
null: true,
name: 'app_id',
tag: 'input',
},
{
display: 'App Secret',
null: true,
name: 'app_secret',
tag: 'input',
},
{
display: 'Site',
null: true,
name: 'site',
tag: 'input',
placeholder: 'https://gitlab.YOURDOMAIN.com',
},
],
},
state: {},
frontend: false
)
Setting.create_or_update(
title: 'Authentication via %s',
name: 'auth_oauth2',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via Generic OAuth2. Register your app first,',
options: {
form: [
{
display: '',
null: true,
name: 'auth_oauth2',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_oauth2_credentials'],
title_i18n: ['Generic OAuth2'],
},
state: false,
frontend: true
)
Setting.create_or_update(
title: 'Generic OAuth2 App Credentials',
name: 'auth_oauth2_credentials',
area: 'Security::ThirdPartyAuthentication::GenericOAuth',
description: 'Enables user authentication via Generic OAuth2.',
options: {
form: [
{
display: 'Name',
null: true,
name: 'name',
tag: 'input',
placeholder: 'Some Provider Name',
},
{
display: 'App ID',
null: true,
name: 'app_id',
tag: 'input',
},
{
display: 'App Secret',
null: true,
name: 'app_secret',
tag: 'input',
},
{
display: 'Site',
null: true,
name: 'site',
tag: 'input',
placeholder: 'https://gitlab.YOURDOMAIN.com',
},
{
display: 'authorize_url',
null: true,
name: 'authorize_url',
tag: 'input',
placeholder: '/oauth/authorize',
},
{
display: 'token_url',
null: true,
name: 'token_url',
tag: 'input',
placeholder: '/oauth/token',
},
],
},
state: {},
frontend: false
)
end
end

View file

@ -110,7 +110,7 @@ Setting.create_if_not_exists(
},
],
},
preferences: { prio: 3 },
preferences: { prio: 3, controller: 'SettingsAreaLogo' },
state: 'logo.svg',
frontend: true
)
@ -432,10 +432,14 @@ Setting.create_if_not_exists(
frontend: true
)
Setting.create_if_not_exists(
title: 'Authentication via LDAP',
title: 'Authentication via %s',
name: 'auth_ldap',
area: 'Security::Authentication',
description: 'Enables user authentication via LDAP.',
description: 'Enables user authentication via %s.',
preferences: {
title_i18n: ['LDAP'],
description_i18n: ['LDAP']
},
state: {
adapter: 'Auth::Ldap',
host: 'localhost',
@ -457,10 +461,10 @@ Setting.create_if_not_exists(
frontend: false
)
Setting.create_if_not_exists(
title: 'Authentication via Twitter',
title: 'Authentication via %s',
name: 'auth_twitter',
area: 'Security::ThirdPartyAuthentication',
description: "@T('Enables user authentication via twitter. Register your app first at [Twitter Developer Site](https://dev.twitter.com/apps)')",
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
@ -475,13 +479,19 @@ Setting.create_if_not_exists(
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_twitter_credentials'],
title_i18n: ['Twitter'],
description_i18n: ['Twitter', 'Twitter Developer Site', 'https://dev.twitter.com/apps']
},
state: false,
frontend: true
)
Setting.create_if_not_exists(
title: 'Twitter App Credentials',
name: 'auth_twitter_credentials',
area: 'Security::ThirdPartyAuthentication',
area: 'Security::ThirdPartyAuthentication::Twitter',
description: 'App credentials for Twitter.',
options: {
form: [
@ -503,10 +513,10 @@ Setting.create_if_not_exists(
frontend: false
)
Setting.create_if_not_exists(
title: 'Authentication via Facebook',
title: 'Authentication via %s',
name: 'auth_facebook',
area: 'Security::ThirdPartyAuthentication',
description: "@T('Enables user authentication via Facebook. Register your app first at [Facebook Developer Site](https://developers.facebook.com/apps/)')",
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
@ -521,6 +531,12 @@ Setting.create_if_not_exists(
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_facebook_credentials'],
title_i18n: ['Facebook'],
description_i18n: ['Facebook', 'Facebook Developer Site', 'https://developers.facebook.com/apps/']
},
state: false,
frontend: true
)
@ -528,7 +544,7 @@ Setting.create_if_not_exists(
Setting.create_if_not_exists(
title: 'Facebook App Credentials',
name: 'auth_facebook_credentials',
area: 'Security::ThirdPartyAuthentication',
area: 'Security::ThirdPartyAuthentication::Facebook',
description: 'App credentials for Facebook.',
options: {
form: [
@ -551,10 +567,10 @@ Setting.create_if_not_exists(
)
Setting.create_if_not_exists(
title: 'Authentication via Google',
title: 'Authentication via %s',
name: 'auth_google_oauth2',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via Google. Register your app first at [Google API Console Site](https://console.developers.google.com/apis/credentials)',
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
@ -569,13 +585,19 @@ Setting.create_if_not_exists(
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_google_oauth2_credentials'],
title_i18n: ['Google'],
description_i18n: ['Google', 'Google API Console Site', 'https://console.developers.google.com/apis/credentials']
},
state: false,
frontend: true
)
Setting.create_if_not_exists(
title: 'Google App Credentials',
name: 'auth_google_oauth2_credentials',
area: 'Security::ThirdPartyAuthentication',
area: 'Security::ThirdPartyAuthentication::Google',
description: 'Enables user authentication via Google.',
options: {
form: [
@ -598,10 +620,10 @@ Setting.create_if_not_exists(
)
Setting.create_if_not_exists(
title: 'Authentication via LinkedIn',
title: 'Authentication via %s',
name: 'auth_linkedin',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via LinkedIn. Register your app first at [Linkedin Developer Site](https://www.linkedin.com/developer/apps)',
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
@ -616,13 +638,19 @@ Setting.create_if_not_exists(
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_linkedin_credentials'],
title_i18n: ['LinkedIn'],
description_i18n: ['LinkedIn', 'Linkedin Developer Site', 'https://www.linkedin.com/developer/apps']
},
state: false,
frontend: true
)
Setting.create_if_not_exists(
title: 'LinkedIn App Credentials',
name: 'auth_linkedin_credentials',
area: 'Security::ThirdPartyAuthentication',
area: 'Security::ThirdPartyAuthentication::Linkedin',
description: 'Enables user authentication via LinkedIn.',
options: {
form: [
@ -644,6 +672,199 @@ Setting.create_if_not_exists(
frontend: false
)
Setting.create_if_not_exists(
title: 'Authentication via %s',
name: 'auth_github',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
display: '',
null: true,
name: 'auth_github',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_github_credentials'],
title_i18n: ['Github'],
description_i18n: ['Github', 'Github OAuth Applications', 'https://github.com/settings/applications']
},
state: false,
frontend: true
)
Setting.create_if_not_exists(
title: 'Github App Credentials',
name: 'auth_github_credentials',
area: 'Security::ThirdPartyAuthentication::Github',
description: 'Enables user authentication via Github.',
options: {
form: [
{
display: 'App ID',
null: true,
name: 'app_id',
tag: 'input',
},
{
display: 'App Secret',
null: true,
name: 'app_secret',
tag: 'input',
},
],
},
state: {},
frontend: false
)
Setting.create_if_not_exists(
title: 'Authentication via %s',
name: 'auth_gitlab',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via %s. Register your app first at [%s](%s).',
options: {
form: [
{
display: '',
null: true,
name: 'auth_gitlab',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_gitlab_credentials'],
title_i18n: ['Gitlab'],
description_i18n: ['Gitlab', 'Gitlab Applications', 'https://your-gitlab-host/admin/applications']
},
state: false,
frontend: true
)
Setting.create_if_not_exists(
title: 'Gitlab App Credentials',
name: 'auth_gitlab_credentials',
area: 'Security::ThirdPartyAuthentication::Gitlab',
description: 'Enables user authentication via Gitlab.',
options: {
form: [
{
display: 'App ID',
null: true,
name: 'app_id',
tag: 'input',
},
{
display: 'App Secret',
null: true,
name: 'app_secret',
tag: 'input',
},
{
display: 'Site',
null: true,
name: 'site',
tag: 'input',
placeholder: 'https://gitlab.YOURDOMAIN.com',
},
],
},
state: {},
frontend: false
)
Setting.create_if_not_exists(
title: 'Authentication via %s',
name: 'auth_oauth2',
area: 'Security::ThirdPartyAuthentication',
description: 'Enables user authentication via Generic OAuth2. Register your app first,',
options: {
form: [
{
display: '',
null: true,
name: 'auth_oauth2',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
preferences: {
controller: 'SettingsAreaSwitch',
sub: ['auth_oauth2_credentials'],
title_i18n: ['Generic OAuth2'],
},
state: false,
frontend: true
)
Setting.create_if_not_exists(
title: 'Generic OAuth2 App Credentials',
name: 'auth_oauth2_credentials',
area: 'Security::ThirdPartyAuthentication::GenericOAuth',
description: 'Enables user authentication via Generic OAuth2.',
options: {
form: [
{
display: 'Name',
null: true,
name: 'name',
tag: 'input',
placeholder: 'Some Provider Name',
},
{
display: 'App ID',
null: true,
name: 'app_id',
tag: 'input',
},
{
display: 'App Secret',
null: true,
name: 'app_secret',
tag: 'input',
},
{
display: 'Site',
null: true,
name: 'site',
tag: 'input',
placeholder: 'https://gitlab.YOURDOMAIN.com',
},
{
display: 'authorize_url',
null: true,
name: 'authorize_url',
tag: 'input',
placeholder: '/oauth/authorize',
},
{
display: 'token_url',
null: true,
name: 'token_url',
tag: 'input',
placeholder: '/oauth/token',
},
],
},
state: {},
frontend: false
)
Setting.create_if_not_exists(
title: 'Minimal size',
name: 'password_min_size',

View file

@ -298,6 +298,15 @@ test( "i18n", function() {
translated = App.i18n.translateContent('<test&now>//*äöüß');
equal( translated, '&lt;test&amp;now&gt;//*äöüß', 'de - <test&now>//*äöüß' );
translated = App.i18n.translateContent('some link [to what ever](http://lalala)');
equal( translated, 'some link <a href="http://lalala" target="_blank">to what ever</a>', 'de-de - link' );
translated = App.i18n.translateContent('some link [to what ever](%s)', 'http://lalala');
equal( translated, 'some link <a href="http://lalala" target="_blank">to what ever</a>', 'de-de - link' );
translated = App.i18n.translateContent('Enables user authentication via %s. Register your app first at [%s](%s).', 'XXX', 'YYY', 'http://lalala');
equal( translated, 'Aktivieren der Benutzeranmeldung über XXX. Registriere Deine Anwendung zuerst über <a href="http://lalala" target="_blank">YYY</a>.', 'en-us - link' );
var time_local = new Date();
var offset = time_local.getTimezoneOffset();
var timestamp = App.i18n.translateTimestamp('2012-11-06T21:07:24Z', offset);
@ -347,6 +356,15 @@ test( "i18n", function() {
translated = App.i18n.translateContent('<test&now>');
equal( translated, '&lt;test&amp;now&gt;', 'en-us - <test&now>' );
translated = App.i18n.translateContent('some link [to what ever](http://lalala)');
equal( translated, 'some link <a href="http://lalala" target="_blank">to what ever</a>', 'en-us - link' );
translated = App.i18n.translateContent('some link [to what ever](%s)', 'http://lalala');
equal( translated, 'some link <a href="http://lalala" target="_blank">to what ever</a>', 'en-us - link' );
translated = App.i18n.translateContent('Enables user authentication via %s. Register your app first at [%s](%s).', 'XXX', 'YYY', 'http://lalala');
equal( translated, 'Enables user authentication via XXX. Register your app first at <a href="http://lalala" target="_blank">YYY</a>.', 'en-us - link' );
timestamp = App.i18n.translateTimestamp('2012-11-06T21:07:24Z', offset);
equal( timestamp, '11/06/2012 21:07', 'en - timestamp translated correctly' );

View file

@ -12,123 +12,132 @@ class SettingTest < TestCase
tasks_close_all()
# make sure, that we have english frontend
click( css: 'a[href="#current_user"]' )
click( css: 'a[href="#profile"]' )
click( css: 'a[href="#profile/language"]' )
click(css: 'a[href="#current_user"]')
click(css: 'a[href="#profile"]')
click(css: 'a[href="#profile/language"]')
select(
css: '.language_item [name="locale"]',
value: 'English (United States)',
)
click( css: '.content button[type="submit"]' )
click(css: '.content button[type="submit"]')
sleep 2
# change settings
click( css: 'a[href="#manage"]' )
click( css: 'a[href="#settings/security"]' )
click( css: 'a[href="#third_party_auth"]' )
click(css: 'a[href="#manage"]')
click(css: 'a[href="#settings/security"]')
click(css: 'a[href="#third_party_auth"]')
sleep 2
switch(
css: '#content .js-setting[data-name="auth_facebook"]',
type: 'off',
)
browser2 = browser_instance
location(
browser: browser2,
url: browser_url,
)
watch_for(
browser: browser2,
css: 'body',
value: 'login',
)
match_not(
browser: browser2,
css: 'body',
value: 'facebook',
)
# set yes
select(
css: '#auth_facebook select[name="auth_facebook"]',
value: 'yes',
)
match(
css: '#auth_facebook select[name="auth_facebook"]',
value: 'yes',
)
click( css: '#auth_facebook button[type=submit]' )
watch_for(
css: '#notify',
value: 'update successful',
)
sleep 4
match(
css: '#auth_facebook select[name="auth_facebook"]',
value: 'yes',
)
match_not(
css: '#auth_facebook select[name="auth_facebook"]',
value: 'no',
)
# set no
select(
css: '#auth_facebook select[name="auth_facebook"]',
value: 'no',
)
click( css: '#auth_facebook button[type=submit]' )
watch_for(
css: '#notify',
value: 'update successful',
)
sleep 4
match(
css: '#auth_facebook select[name="auth_facebook"]',
value: 'no',
)
match_not(
css: '#auth_facebook select[name="auth_facebook"]',
value: 'yes',
switch(
css: '#content .js-setting[data-name="auth_facebook"]',
type: 'on',
)
# set key and secret
set(
css: '#auth_facebook_credentials input[name=app_id]',
css: '[data-name="auth_facebook_credentials"] input[name=app_id]',
value: 'id_test1234äöüß',
)
set(
css: '#auth_facebook_credentials input[name=app_secret]',
css: '[data-name="auth_facebook_credentials"] input[name=app_secret]',
value: 'secret_test1234äöüß',
)
click( css: '#auth_facebook_credentials button[type=submit]' )
click( css: '[data-name="auth_facebook_credentials"] button[type=submit]')
watch_for(
css: '#notify',
value: 'update successful',
)
sleep 4
match(
css: '#auth_facebook_credentials input[name=app_id]',
css: '[data-name="auth_facebook_credentials"] input[name=app_id]',
value: 'id_test1234äöüß',
)
match(
css: '#auth_facebook_credentials input[name=app_secret]',
css: '[data-name="auth_facebook_credentials"] input[name=app_secret]',
value: 'secret_test1234äöüß',
)
# verify login page
sleep 2
watch_for(
browser: browser2,
css: 'body',
value: 'facebook',
)
# set key and secret again
set(
css: '#auth_facebook_credentials input[name=app_id]',
css: '[data-name="auth_facebook_credentials"] input[name=app_id]',
value: '---',
)
set(
css: '#auth_facebook_credentials input[name=app_secret]',
css: '[data-name="auth_facebook_credentials"] input[name=app_secret]',
value: '---',
)
click( css: '#auth_facebook_credentials button[type=submit]' )
click(css: '[data-name="auth_facebook_credentials"] button[type=submit]')
watch_for(
css: '#notify',
value: 'update successful',
)
sleep 4
match(
css: '#auth_facebook_credentials input[name=app_id]',
css: '[data-name="auth_facebook_credentials"] input[name=app_id]',
value: '---',
)
match(
css: '#auth_facebook_credentials input[name=app_secret]',
css: '[data-name="auth_facebook_credentials"] input[name=app_secret]',
value: '---',
)
reload()
click(css: 'a[href="#settings/security"]')
click(css: 'a[href="#third_party_auth"]')
watch_for(
css: '#auth_facebook_credentials input[name=app_id]',
css: '[data-name="auth_facebook_credentials"] input[name=app_id]',
value: '---',
)
watch_for(
css: '#auth_facebook_credentials input[name=app_secret]',
css: '[data-name="auth_facebook_credentials"] input[name=app_secret]',
value: '---',
)
sleep 2
switch(
css: '#content .js-setting[data-name="auth_facebook"]',
type: 'off',
)
sleep 2
watch_for(
browser: browser2,
css: 'body',
value: 'login',
)
match_not(
browser: browser2,
css: 'body',
value: 'facebook',
)
end
end