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) $('.modal .modal-body').html(finish)
) )
@delay(create, 1800) @delay(create, 2800)
template template
testTicketFinish: (data) -> testTicketFinish: (data) ->

View file

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

View file

@ -4,7 +4,11 @@ class App.SettingsArea extends App.Controller
# check authentication # check authentication
@authenticateCheckRedirect() @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: => render: =>
@ -31,179 +35,10 @@ class App.SettingsArea extends App.Controller
elements = [] elements = []
for setting in settings for setting in settings
if setting.name is 'product_logo' if setting.preferences.controller && App[setting.preferences.controller]
item = new App.SettingsAreaLogo(setting: setting) item = new App[setting.preferences.controller](setting: setting)
else else
item = new App.SettingsAreaItem(setting: setting) item = new App.SettingsAreaItem(setting: setting)
elements.push item.el elements.push item.el
@html elements @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 # observe config changes related to login page
@bind('config_update_local', (data) => @bind('config_update_local', (data) =>
return if data.name != 'maintenance_mode' && return if !data.name.match(/^maintenance/) &&
data.name != 'maintenance_login' && !data.name.match(/^auth/) &&
data.name != 'maintenance_login_message' &&
data.name != 'user_lost_password' && data.name != 'user_lost_password' &&
data.name != 'user_create_account' && data.name != 'user_create_account' &&
data.name != 'product_name' && data.name != 'product_name' &&
@ -38,33 +37,45 @@ class Index extends App.ControllerContent
render: (data = {}) -> render: (data = {}) ->
auth_provider_all = { auth_provider_all = {
facebook: { facebook: {
url: '/auth/facebook', url: '/auth/facebook'
name: 'Facebook', name: 'Facebook'
config: 'auth_facebook', config: 'auth_facebook'
class: 'facebook', class: 'facebook'
}, },
twitter: { twitter: {
url: '/auth/twitter', url: '/auth/twitter'
name: 'Twitter', name: 'Twitter'
config: 'auth_twitter', config: 'auth_twitter'
class: 'twitter', class: 'twitter'
}, },
linkedin: { linkedin: {
url: '/auth/linkedin', url: '/auth/linkedin'
name: 'LinkedIn', name: 'LinkedIn'
config: 'auth_linkedin', config: 'auth_linkedin'
class: '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: { google_oauth2: {
url: '/auth/google_oauth2', url: '/auth/google_oauth2'
name: 'Google', name: 'Google'
config: 'auth_google_oauth2', config: 'auth_google_oauth2'
class: 'google', class: 'google'
}, },
} }
auth_providers = [] auth_providers = []
for key, provider of auth_provider_all 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 auth_providers.push provider
@html App.view('login')( @html App.view('login')(

View file

@ -218,7 +218,7 @@ class _i18nSingleton extends Spine.Module
if quote if quote
translated = App.Utils.htmlEscape(translated) translated = App.Utils.htmlEscape(translated)
# apply inline markup # apply inline markup pre
if markup if markup
translated = translated translated = translated
.replace(/\|\|(.+?)\|\|/gm, '<i>$1</i>') .replace(/\|\|(.+?)\|\|/gm, '<i>$1</i>')
@ -241,6 +241,11 @@ class _i18nSingleton extends Spine.Module
"<a href=\"#{arg}\">🔗</a>" "<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 @log 'debug', 'translate', string, args, translated
# return translated string # 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('A Test Ticket has been created, you can find it in your overview "%s" %l.', @overviewName, @overviewUrl) %></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> <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', state: 'logo.svg',
frontend: true frontend: true
) )
@ -432,10 +432,14 @@ Setting.create_if_not_exists(
frontend: true frontend: true
) )
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Authentication via LDAP', title: 'Authentication via %s',
name: 'auth_ldap', name: 'auth_ldap',
area: 'Security::Authentication', area: 'Security::Authentication',
description: 'Enables user authentication via LDAP.', description: 'Enables user authentication via %s.',
preferences: {
title_i18n: ['LDAP'],
description_i18n: ['LDAP']
},
state: { state: {
adapter: 'Auth::Ldap', adapter: 'Auth::Ldap',
host: 'localhost', host: 'localhost',
@ -457,10 +461,10 @@ Setting.create_if_not_exists(
frontend: false frontend: false
) )
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Authentication via Twitter', title: 'Authentication via %s',
name: 'auth_twitter', name: 'auth_twitter',
area: 'Security::ThirdPartyAuthentication', 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: { options: {
form: [ 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, state: false,
frontend: true frontend: true
) )
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Twitter App Credentials', title: 'Twitter App Credentials',
name: 'auth_twitter_credentials', name: 'auth_twitter_credentials',
area: 'Security::ThirdPartyAuthentication', area: 'Security::ThirdPartyAuthentication::Twitter',
description: 'App credentials for Twitter.', description: 'App credentials for Twitter.',
options: { options: {
form: [ form: [
@ -503,10 +513,10 @@ Setting.create_if_not_exists(
frontend: false frontend: false
) )
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Authentication via Facebook', title: 'Authentication via %s',
name: 'auth_facebook', name: 'auth_facebook',
area: 'Security::ThirdPartyAuthentication', 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: { options: {
form: [ 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, state: false,
frontend: true frontend: true
) )
@ -528,7 +544,7 @@ Setting.create_if_not_exists(
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Facebook App Credentials', title: 'Facebook App Credentials',
name: 'auth_facebook_credentials', name: 'auth_facebook_credentials',
area: 'Security::ThirdPartyAuthentication', area: 'Security::ThirdPartyAuthentication::Facebook',
description: 'App credentials for Facebook.', description: 'App credentials for Facebook.',
options: { options: {
form: [ form: [
@ -551,10 +567,10 @@ Setting.create_if_not_exists(
) )
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Authentication via Google', title: 'Authentication via %s',
name: 'auth_google_oauth2', name: 'auth_google_oauth2',
area: 'Security::ThirdPartyAuthentication', 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: { options: {
form: [ 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, state: false,
frontend: true frontend: true
) )
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Google App Credentials', title: 'Google App Credentials',
name: 'auth_google_oauth2_credentials', name: 'auth_google_oauth2_credentials',
area: 'Security::ThirdPartyAuthentication', area: 'Security::ThirdPartyAuthentication::Google',
description: 'Enables user authentication via Google.', description: 'Enables user authentication via Google.',
options: { options: {
form: [ form: [
@ -598,10 +620,10 @@ Setting.create_if_not_exists(
) )
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Authentication via LinkedIn', title: 'Authentication via %s',
name: 'auth_linkedin', name: 'auth_linkedin',
area: 'Security::ThirdPartyAuthentication', 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: { options: {
form: [ 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, state: false,
frontend: true frontend: true
) )
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'LinkedIn App Credentials', title: 'LinkedIn App Credentials',
name: 'auth_linkedin_credentials', name: 'auth_linkedin_credentials',
area: 'Security::ThirdPartyAuthentication', area: 'Security::ThirdPartyAuthentication::Linkedin',
description: 'Enables user authentication via LinkedIn.', description: 'Enables user authentication via LinkedIn.',
options: { options: {
form: [ form: [
@ -644,6 +672,199 @@ Setting.create_if_not_exists(
frontend: false 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( Setting.create_if_not_exists(
title: 'Minimal size', title: 'Minimal size',
name: 'password_min_size', name: 'password_min_size',

View file

@ -298,6 +298,15 @@ test( "i18n", function() {
translated = App.i18n.translateContent('<test&now>//*äöüß'); translated = App.i18n.translateContent('<test&now>//*äöüß');
equal( translated, '&lt;test&amp;now&gt;//*äöüß', 'de - <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 time_local = new Date();
var offset = time_local.getTimezoneOffset(); var offset = time_local.getTimezoneOffset();
var timestamp = App.i18n.translateTimestamp('2012-11-06T21:07:24Z', offset); var timestamp = App.i18n.translateTimestamp('2012-11-06T21:07:24Z', offset);
@ -347,6 +356,15 @@ test( "i18n", function() {
translated = App.i18n.translateContent('<test&now>'); translated = App.i18n.translateContent('<test&now>');
equal( translated, '&lt;test&amp;now&gt;', 'en-us - <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); timestamp = App.i18n.translateTimestamp('2012-11-06T21:07:24Z', offset);
equal( timestamp, '11/06/2012 21:07', 'en - timestamp translated correctly' ); equal( timestamp, '11/06/2012 21:07', 'en - timestamp translated correctly' );

View file

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