Merge branch 'v2.0'
This commit is contained in:
commit
4fcf77e721
|
@ -15,3 +15,4 @@
|
|||
--noopening-brace-on-new-line
|
||||
--stack-opening-tokens
|
||||
--format-skipping
|
||||
--continuation-indentation=2
|
||||
|
|
3
Makefile
3
Makefile
|
@ -1046,10 +1046,11 @@ zip-dist:
|
|||
manifest: configure
|
||||
@for i in ${SRCCOMMONDIR} ${SRCHANDLERDIR} ${SRCPORTALDIR} ${SRCMANAGERDIR}; do \
|
||||
cd $$i; \
|
||||
rm -vf MANIFEST*; \
|
||||
rm -vf MANIFEST MANIFEST*bak; \
|
||||
make manifest; \
|
||||
cd -; \
|
||||
done
|
||||
perl -i -ne 'print unless/proverc/' */MANIFEST
|
||||
|
||||
cpan: clean configure common_cpan handler_cpan portal_cpan manager_cpan
|
||||
for i in Common Portal Handler Manager; do \
|
||||
|
|
|
@ -119,8 +119,13 @@
|
|||
"namespace" : "lemonldap-ng-sessions"
|
||||
},
|
||||
"locationRules" : {
|
||||
"auth.example.com" : {
|
||||
"(?#checkUser)^/checkuser": "$uid eq \"dwho\"",
|
||||
"(?#errors)^/lmerror/": "accept",
|
||||
"default" : "accept"
|
||||
},
|
||||
"manager.__DNSDOMAIN__" : {
|
||||
"(?#Configuration)^/(manager\\.html|conf/)" : "$uid eq \"dwho\"",
|
||||
"(?#Configuration)^/(manager\\.html|$)" : "$uid eq \"dwho\"",
|
||||
"(?#Notifications)/notifications" : "$uid eq \"dwho\" or $uid eq \"rtyler\"",
|
||||
"(?#Sessions)/sessions" : "$uid eq \"dwho\" or $uid eq \"rtyler\"",
|
||||
"default" : "$uid eq \"dwho\" or $uid eq \"rtyler\""
|
||||
|
|
|
@ -10,8 +10,12 @@ TESTDIR=${BASE}/${TYPE}.d
|
|||
|
||||
LLSOURCEDIR=`pwd`
|
||||
|
||||
LIST=$2
|
||||
|
||||
test "$LIST" == "" 2>/dev/null && LIST=lemonldap-ng-*
|
||||
|
||||
EXITCODE=0
|
||||
for LLLIB in lemonldap-ng-*; do
|
||||
for LLLIB in $LIST; do
|
||||
mkdir -p $LLSOURCEDIR/$LLLIB/debian/tests/pkg-perl
|
||||
for llfile in debian/tests/pkg-perl/${LLLIB}*; do
|
||||
if [ -r $llfile ]; then
|
||||
|
|
|
@ -38,7 +38,7 @@ useRedirectOnError = 0
|
|||
|
||||
[manager]
|
||||
|
||||
enabledModules = conf, sessions, notifications, 2ndFA
|
||||
enabledModules = conf, sessions, notifications, 2ndFA, viewer
|
||||
protection = manager
|
||||
staticPrefix = /static
|
||||
languages = fr, en, vi, ar, de, it, zh
|
||||
|
|
|
@ -147,7 +147,7 @@
|
|||
"default" : "accept"
|
||||
},
|
||||
"manager.example.com": {
|
||||
"(?#Configuration)^/(manager\\.html|conf/)": "$uid eq \"dwho\"",
|
||||
"(?#Configuration)^/(manager\\.html|$)": "$uid eq \"dwho\"",
|
||||
"(?#Notifications)^/notifications": "$uid eq \"dwho\" or $uid eq \"rtyler\"",
|
||||
"(?#Sessions)^/sessions": "$uid eq \"dwho\" or $uid eq \"rtyler\"",
|
||||
"default": "$uid eq \"dwho\" or $uid eq \"rtyler\""
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
--blib
|
|
@ -51,6 +51,7 @@
|
|||
"Crypt::OpenSSL::RSA" : "0",
|
||||
"Crypt::OpenSSL::X509" : "0",
|
||||
"Crypt::Rijndael" : "0",
|
||||
"Crypt::URandom" : "0",
|
||||
"Digest::SHA" : "0",
|
||||
"HTML::Template" : "0",
|
||||
"JSON" : "0",
|
||||
|
|
|
@ -36,6 +36,7 @@ requires:
|
|||
Crypt::OpenSSL::RSA: '0'
|
||||
Crypt::OpenSSL::X509: '0'
|
||||
Crypt::Rijndael: '0'
|
||||
Crypt::URandom: '0'
|
||||
Digest::SHA: '0'
|
||||
HTML::Template: '0'
|
||||
JSON: '0'
|
||||
|
|
|
@ -360,7 +360,11 @@ languages = fr, en, vi, ar
|
|||
; Manager modules enabled
|
||||
; Set here the list of modules you want to see in manager interface
|
||||
; The first will be used as default module displayed
|
||||
enabledModules = conf, sessions, notifications, 2ndFA
|
||||
enabledModules = conf, sessions, notifications, 2ndFA, viewer
|
||||
|
||||
; Viewer options - Default values
|
||||
;viewerHiddenPK = samlIDPMetaDataNodes samlSPMetaDataNodes
|
||||
;viewerAllowBrowser = 0
|
||||
|
||||
;[node-handler]
|
||||
;
|
||||
|
|
|
@ -296,13 +296,14 @@ sub defaultValues {
|
|||
'useRedirectOnError' => 1,
|
||||
'useSafeJail' => 1,
|
||||
'utotp2fActivation' => 0,
|
||||
'webIDAuthnLevel' => 1,
|
||||
'webIDExportedVars' => {},
|
||||
'whatToTrace' => 'uid',
|
||||
'yubikey2fActivation' => 0,
|
||||
'yubikey2fPublicIDSize' => 12,
|
||||
'yubikey2fSelfRegistration' => 0,
|
||||
'yubikey2fUserCanRemoveKey' => 1
|
||||
'viewerHiddenPK' => 'samlIDPMetaDataNodes samlSPMetaDataNodes',
|
||||
'webIDAuthnLevel' => 1,
|
||||
'webIDExportedVars' => {},
|
||||
'whatToTrace' => 'uid',
|
||||
'yubikey2fActivation' => 0,
|
||||
'yubikey2fPublicIDSize' => 12,
|
||||
'yubikey2fSelfRegistration' => 0,
|
||||
'yubikey2fUserCanRemoveKey' => 1
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
-I ../lemonldap-ng-common/blib/lib
|
||||
-I .
|
||||
--blib
|
|
@ -0,0 +1,4 @@
|
|||
-I .
|
||||
-I ../lemonldap-ng-common/blib/lib
|
||||
-I ../lemonldap-ng-handler/blib/lib
|
||||
--blib
|
|
@ -23,6 +23,7 @@ extends 'Lemonldap::NG::Common::Conf::AccessLib',
|
|||
'Lemonldap::NG::Handler::PSGI::Router';
|
||||
|
||||
has csp => ( is => 'rw' );
|
||||
has brw => ( is => 'rw', default => 0 );
|
||||
|
||||
## @method boolean init($args)
|
||||
# Launch initialization method
|
||||
|
@ -86,7 +87,7 @@ sub init {
|
|||
$self->csp(
|
||||
"default-src 'self' $portal;frame-ancestors 'none';form-action 'self';"
|
||||
);
|
||||
|
||||
$self->brw( $conf->{viewerAllowBrowser} );
|
||||
$self->defaultRoute( $working[0]->defaultRoute );
|
||||
|
||||
# Find out more glyphicones at https://www.w3schools.com/icons/bootstrap_icons_glyphicons.asp
|
||||
|
@ -94,7 +95,8 @@ sub init {
|
|||
'conf' => 'cog',
|
||||
'sessions' => 'duplicate',
|
||||
'notifications' => 'bell',
|
||||
'2ndFA' => 'wrench'
|
||||
'2ndFA' => 'wrench',
|
||||
'viewer' => 'eye-open',
|
||||
};
|
||||
|
||||
$self->links( [] );
|
||||
|
@ -132,13 +134,14 @@ sub init {
|
|||
}
|
||||
|
||||
sub tplParams {
|
||||
return ( VERSION => $VERSION, );
|
||||
my ($self) = @_;
|
||||
return ( VERSION => $VERSION, ALLOWBROWSER => $self->brw );
|
||||
}
|
||||
|
||||
sub javascript {
|
||||
my ($self) = @_;
|
||||
return
|
||||
'var formPrefix=staticPrefix+"forms/";var confPrefix=scriptname+"confs/";'
|
||||
'var formPrefix=staticPrefix+"forms/";var confPrefix=scriptname+"confs/";var viewPrefix=scriptname+"view/";'
|
||||
. ( $self->links ? 'var links=' . to_json( $self->links ) . ';' : '' )
|
||||
. (
|
||||
$self->menuLinks
|
||||
|
|
|
@ -3558,6 +3558,14 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
|
|||
],
|
||||
'type' => 'select'
|
||||
},
|
||||
'viewerAllowBrowser' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'viewerHiddenPK' => {
|
||||
'default' => 'samlIDPMetaDataNodes samlSPMetaDataNodes',
|
||||
'type' => 'text'
|
||||
},
|
||||
'virtualHosts' => {
|
||||
'type' => 'virtualHostContainer'
|
||||
},
|
||||
|
|
|
@ -915,6 +915,19 @@ sub attributes {
|
|||
flags => 'hp',
|
||||
},
|
||||
|
||||
# Viewer
|
||||
viewerHiddenPK => {
|
||||
type => 'text',
|
||||
default => 'samlIDPMetaDataNodes samlSPMetaDataNodes',
|
||||
documentation => 'ConfTree hidden primary keys',
|
||||
flags => 'm',
|
||||
},
|
||||
viewerAllowBrowser => {
|
||||
type => 'bool',
|
||||
default => 0,
|
||||
documentation => 'Allow configuration browser',
|
||||
},
|
||||
|
||||
# Notification
|
||||
oldNotifFormat => {
|
||||
type => 'bool',
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
# This module implements all the methods that responds to '/confs/*' requests
|
||||
# It contains 2 sections:
|
||||
# - initialization methods
|
||||
# - upload method
|
||||
#
|
||||
# Read methods are inherited from Lemonldap::NG::Common::Conf::RESTServer
|
||||
package Lemonldap::NG::Manager::Viewer;
|
||||
|
||||
use 5.10.0;
|
||||
use utf8;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Common::Conf::Constants;
|
||||
use Lemonldap::NG::Common::UserAgent;
|
||||
use URI::URL;
|
||||
|
||||
use feature 'state';
|
||||
|
||||
extends 'Lemonldap::NG::Manager::Conf';
|
||||
|
||||
our $VERSION = '2.0.3';
|
||||
|
||||
#############################
|
||||
# I. INITIALIZATION METHODS #
|
||||
#############################
|
||||
|
||||
use constant defaultRoute => 'viewer.html';
|
||||
|
||||
has ua => ( is => 'rw' );
|
||||
|
||||
sub addRoutes {
|
||||
my ( $self, $conf ) = @_;
|
||||
$self->ua( Lemonldap::NG::Common::UserAgent->new($conf) );
|
||||
|
||||
my @enabledPK = ();
|
||||
my @keys = qw(virtualHosts samlIDPMetaDataNodes samlSPMetaDataNodes
|
||||
applicationList oidcOPMetaDataNodes oidcRPMetaDataNodes
|
||||
casSrvMetaDataNodes casAppMetaDataNodes
|
||||
authChoiceModules grantSessionRules combModules
|
||||
openIdIDPList);
|
||||
|
||||
foreach (@keys) {
|
||||
|
||||
# Ignore hidden ConfTree Primary Keys
|
||||
push @enabledPK, $_
|
||||
unless ( $conf->{viewerHiddenPK} =~ /\b$_\b/ );
|
||||
}
|
||||
|
||||
# HTML template
|
||||
$self->addRoute( 'viewer.html', undef, ['GET'] )
|
||||
|
||||
# READ
|
||||
# Special keys
|
||||
->addRoute(
|
||||
view => {
|
||||
':cfgNum' => \@enabledPK
|
||||
},
|
||||
['GET']
|
||||
)
|
||||
|
||||
# Other keys
|
||||
->addRoute( view => { ':cfgNum' => { '*' => 'getKey' } }, ['GET'] )
|
||||
|
||||
# Difference between confs
|
||||
->addRoute( diff => { ':conf1' => { ':conf2' => 'diff' } } )
|
||||
->addRoute( 'diff.html', undef, ['GET'] );
|
||||
}
|
||||
|
||||
sub getConfByNum {
|
||||
my ( $self, $cfgNum, @args ) = @_;
|
||||
$self->SUPER::getConfByNum( $cfgNum, @args );
|
||||
}
|
||||
|
||||
sub diff {
|
||||
my ( $self, $req, @path ) = @_;
|
||||
$self->SUPER::diff( $req, @path );
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,476 @@
|
|||
###
|
||||
LemonLDAP::NG Viewer client
|
||||
|
||||
This is the main app file. Other are:
|
||||
- struct.json and js/confTree.js that contains the full tree
|
||||
- translate.json that contains the keywords translation
|
||||
|
||||
This file contains:
|
||||
- the AngularJS controller
|
||||
###
|
||||
|
||||
llapp = angular.module 'llngManager', ['ui.tree', 'ui.bootstrap', 'llApp', 'ngCookies']
|
||||
|
||||
###
|
||||
Main AngularJS controller
|
||||
###
|
||||
|
||||
llapp.controller 'TreeCtrl', [
|
||||
'$scope', '$http', '$location', '$q', '$uibModal', '$translator', '$cookies', '$htmlParams',
|
||||
($scope, $http, $location, $q, $uibModal, $translator, $cookies, $htmlParams) ->
|
||||
$scope.links = window.links
|
||||
$scope.menu = $htmlParams.menu
|
||||
$scope.menulinks = window.menulinks
|
||||
$scope.staticPrefix = window.staticPrefix
|
||||
$scope.formPrefix = window.formPrefix
|
||||
$scope.availableLanguages = window.availableLanguages
|
||||
$scope.waiting = true
|
||||
$scope.showM = false
|
||||
$scope.showT = false
|
||||
$scope.form = 'home'
|
||||
$scope.currentCfg = {}
|
||||
$scope.viewPrefix = window.viewPrefix
|
||||
$scope.message = {}
|
||||
$scope.result = ''
|
||||
|
||||
# Import translations functions
|
||||
$scope.translateTitle = (node) ->
|
||||
return $translator.translateField node, 'title'
|
||||
$scope.translateP = $translator.translateP
|
||||
$scope.translate = $translator.translate
|
||||
|
||||
# HELP DISPLAY
|
||||
$scope.helpUrl = 'start.html#configuration'
|
||||
$scope.setShowHelp = (val) ->
|
||||
val = !$scope.showH unless val?
|
||||
$scope.showH = val
|
||||
d = new Date(Date.now())
|
||||
d.setFullYear(d.getFullYear() + 1)
|
||||
$cookies.put 'showhelp', (if val then 'true' else 'false'), {"expires": d}
|
||||
$scope.showH = if $cookies.get('showhelp') == 'false' then false else true
|
||||
$scope.setShowHelp(true) unless $scope.showH?
|
||||
|
||||
# INTERCEPT AJAX ERRORS
|
||||
readError = (response) ->
|
||||
e = response.status
|
||||
j = response.statusLine
|
||||
$scope.waiting = false
|
||||
if e == 403
|
||||
$scope.message =
|
||||
title: 'forbidden'
|
||||
message: ''
|
||||
items: []
|
||||
else if e == 401
|
||||
console.log 'Authentication needed'
|
||||
$scope.message =
|
||||
title: 'authenticationNeeded'
|
||||
message: '__waitOrF5__'
|
||||
items: []
|
||||
else if e == 400
|
||||
$scope.message =
|
||||
title: 'badRequest'
|
||||
message: j
|
||||
items: []
|
||||
else if e > 0
|
||||
$scope.message =
|
||||
title: 'badRequest'
|
||||
message: j
|
||||
items: []
|
||||
else
|
||||
$scope.message =
|
||||
title: 'networkProblem'
|
||||
message: ''
|
||||
items: []
|
||||
return $scope.showModal 'message.html'
|
||||
|
||||
# Modal launcher
|
||||
$scope.showModal = (tpl, init) ->
|
||||
modalInstance = $uibModal.open
|
||||
templateUrl: tpl
|
||||
controller: 'ModalInstanceCtrl'
|
||||
size: 'lg'
|
||||
resolve:
|
||||
elem: ->
|
||||
return (s) ->
|
||||
return $scope[s]
|
||||
set: ->
|
||||
return (f, s) ->
|
||||
$scope[f] = s
|
||||
init: ->
|
||||
return init
|
||||
d = $q.defer()
|
||||
modalInstance.result.then (msgok) ->
|
||||
$scope.message =
|
||||
title: ''
|
||||
message: ''
|
||||
items: []
|
||||
d.resolve msgok
|
||||
,(msgnok) ->
|
||||
$scope.message =
|
||||
title: ''
|
||||
message: ''
|
||||
items: []
|
||||
d.reject msgnok
|
||||
return d.promise
|
||||
|
||||
# FORM DISPLAY FUNCTIONS
|
||||
|
||||
# Function called when a menu item is selected. It launch function stored in
|
||||
# "action" or "title"
|
||||
$scope.menuClick = (button) ->
|
||||
if button.popup
|
||||
window.open button.popup
|
||||
else
|
||||
button.action = button.title unless button.action
|
||||
switch typeof button.action
|
||||
when 'function'
|
||||
button.action $scope.currentNode, $scope
|
||||
when 'string'
|
||||
$scope[button.action]()
|
||||
else
|
||||
console.log typeof button.action
|
||||
$scope.showM = false
|
||||
|
||||
# Display main form
|
||||
$scope.home = ->
|
||||
$scope.form = 'home'
|
||||
$scope.showM = false
|
||||
|
||||
# SAVE FUNCTIONS
|
||||
|
||||
# # Private method called by $scope.save()
|
||||
# _checkSaveResponse = (data) ->
|
||||
# $scope.message =
|
||||
# title: ''
|
||||
# message: ''
|
||||
# items: []
|
||||
# $scope.confirmNeeded = true if data.needConfirm
|
||||
# $scope.message.message = data.message if data.message
|
||||
# if data.details
|
||||
# for m of data.details when m != '__changes__'
|
||||
# if m == '__needConfirmation__'
|
||||
# $scope.message.items.unshift
|
||||
# message: m
|
||||
# items: data.details[m]
|
||||
# else
|
||||
# $scope.message.items.push
|
||||
# message: m
|
||||
# items: data.details[m]
|
||||
# $scope.waiting = false
|
||||
# if data.result == 1
|
||||
# # Force reloading page
|
||||
# $location.path '/confs/'
|
||||
# $scope.message.title = 'successfullySaved'
|
||||
# else
|
||||
# $scope.message.title = 'saveReport'
|
||||
# $scope.showModal 'message.html'
|
||||
|
||||
# Download raw conf
|
||||
$scope.downloadConf = () ->
|
||||
window.open $scope.viewPrefix + $scope.currentCfg.cfgNum + '?full=1'
|
||||
|
||||
|
||||
|
||||
# NODES MANAGEMENT
|
||||
id = 1
|
||||
$scope._findContainer = ->
|
||||
return $scope._findScopeContainer().$modelValue
|
||||
$scope._findScopeContainer = ->
|
||||
cs = $scope.currentScope
|
||||
while not cs.$modelValue.type.match(/Container$/)
|
||||
cs = cs.$parentNodeScope
|
||||
return cs
|
||||
$scope._findScopeByKey = (k) ->
|
||||
cs = $scope.currentScope
|
||||
while not (cs.$modelValue.title == k)
|
||||
cs = cs.$parentNodeScope
|
||||
return cs
|
||||
|
||||
_getAll = (node) ->
|
||||
d = $q.defer()
|
||||
d2 = $q.defer()
|
||||
if node._nodes
|
||||
_stoggle node
|
||||
d.resolve()
|
||||
else if node.cnodes
|
||||
_download(node).then ->
|
||||
d.resolve()
|
||||
else if node.nodes or node.data
|
||||
d.resolve()
|
||||
else
|
||||
$scope.getKey(node).then ->
|
||||
d.resolve()
|
||||
d.promise.then ->
|
||||
t = []
|
||||
if node.nodes
|
||||
for n in node.nodes
|
||||
t.push _getAll(n)
|
||||
$q.all(t).then ->
|
||||
d2.resolve()
|
||||
return d2.promise
|
||||
|
||||
$scope.down = ->
|
||||
id = $scope.currentNode.id
|
||||
p = $scope.currentScope.$parentNodeScope.$modelValue
|
||||
ind = p.nodes.length
|
||||
for n, i in p.nodes
|
||||
if n.id == id then ind = i
|
||||
if ind < p.nodes.length - 1
|
||||
tmp = p.nodes[ind]
|
||||
p.nodes[ind] = p.nodes[ind + 1]
|
||||
p.nodes[ind + 1] = tmp
|
||||
ind
|
||||
|
||||
$scope.up = ->
|
||||
id = $scope.currentNode.id
|
||||
p = $scope.currentScope.$parentNodeScope.$modelValue
|
||||
ind = -1
|
||||
for n, i in p.nodes
|
||||
if n.id == id then ind = i
|
||||
if ind > 0
|
||||
tmp = p.nodes[ind]
|
||||
p.nodes[ind] = p.nodes[ind - 1]
|
||||
p.nodes[ind - 1] = tmp
|
||||
ind
|
||||
|
||||
# test if value is in select
|
||||
$scope.inSelect = (value) ->
|
||||
for n in $scope.currentNode.select
|
||||
return true if n.k == value
|
||||
return false
|
||||
|
||||
# This is for rule form: title = comment if defined, else title = re
|
||||
$scope.changeRuleTitle = (node) ->
|
||||
node.title = if node.comment.length > 0 then node.comment else node.re
|
||||
|
||||
# Node opening
|
||||
|
||||
# authParams mechanism: show used auth modules only (launched by stoggle)
|
||||
$scope.filters = {}
|
||||
$scope.execFilters = (scope) ->
|
||||
scope = if scope then scope else $scope
|
||||
for filter,func of $scope.filters
|
||||
if $scope.filters.hasOwnProperty(filter)
|
||||
return window.filterFunctions[filter](scope, $q, func)
|
||||
false
|
||||
|
||||
# To avoid binding all the tree, nodes are pushed in DOM only when opened
|
||||
$scope.stoggle = (scope) ->
|
||||
node = scope.$modelValue
|
||||
_stoggle node
|
||||
scope.toggle()
|
||||
_stoggle = (node) ->
|
||||
for n in ['nodes', 'nodes_cond']
|
||||
if node["_#{n}"]
|
||||
node[n] = []
|
||||
for a in node["_#{n}"]
|
||||
node[n].push a
|
||||
delete node["_#{n}"]
|
||||
# Call execFilter for authParams
|
||||
if node._nodes_filter
|
||||
if node.nodes
|
||||
for n in node.nodes
|
||||
n.onChange = $scope.execFilters
|
||||
$scope.filters[node._nodes_filter] = node
|
||||
$scope.execFilters()
|
||||
|
||||
# Simple toggle management
|
||||
$scope.toggle = (scope) ->
|
||||
scope.toggle()
|
||||
|
||||
# cnodes management: hash keys/values are loaded when parent node is opened
|
||||
$scope.download = (scope) ->
|
||||
node = scope.$modelValue
|
||||
return _download(node)
|
||||
_download = (node) ->
|
||||
d = $q.defer()
|
||||
d.notify 'Trying to get datas'
|
||||
$scope.waiting = true
|
||||
$http.get("#{window.viewPrefix}#{$scope.currentCfg.cfgNum}/#{node.cnodes}").then (response) ->
|
||||
data = response.data
|
||||
# Manage datas errors
|
||||
if not data
|
||||
d.reject 'Empty response from server'
|
||||
else if data.error
|
||||
if data.error.match(/setDefault$/)
|
||||
if node['default']
|
||||
node.nodes = node['default'].slice(0)
|
||||
else
|
||||
node.nodes = []
|
||||
delete node.cnodes
|
||||
d.resolve 'Set data to default value'
|
||||
else
|
||||
d.reject "Server return an error: #{data.error}"
|
||||
else
|
||||
# Store datas
|
||||
delete node.cnodes
|
||||
if not node.type
|
||||
node.type = 'keyTextContainer'
|
||||
node.nodes = []
|
||||
# TODO: try/catch
|
||||
for a in data
|
||||
if a.template
|
||||
a._nodes = templates a.template, a.title
|
||||
node.nodes.push a
|
||||
d.resolve 'OK'
|
||||
$scope.waiting = false
|
||||
, (response) ->
|
||||
readError response
|
||||
d.reject ''
|
||||
return d.promise
|
||||
|
||||
$scope.openCnode = (scope) ->
|
||||
$scope.download(scope).then ->
|
||||
scope.toggle()
|
||||
|
||||
setHelp = (scope) ->
|
||||
while !scope.$modelValue.help and scope.$parentNodeScope
|
||||
scope = scope.$parentNodeScope
|
||||
$scope.helpUrl = scope.$modelValue.help || 'start.html#configuration'
|
||||
|
||||
$scope.displayForm = (scope) ->
|
||||
node = scope.$modelValue
|
||||
if node.cnodes
|
||||
$scope.download scope
|
||||
if node._nodes
|
||||
$scope.stoggle scope
|
||||
$scope.currentNode = node
|
||||
$scope.currentScope = scope
|
||||
f = if node.type then node.type else 'text'
|
||||
if node.nodes || node._nodes || node.cnodes
|
||||
$scope.form = if f != 'text' then f else 'mini'
|
||||
else
|
||||
$scope.form = f
|
||||
# Get datas
|
||||
$scope.getKey node
|
||||
if node.type and node.type == 'simpleInputContainer'
|
||||
for n in node.nodes
|
||||
$scope.getKey(n)
|
||||
$scope.showT = false
|
||||
setHelp scope
|
||||
|
||||
# method `getKey()`:
|
||||
# - return a promise with the data:
|
||||
# - from node when set
|
||||
# - after downloading else
|
||||
#
|
||||
$scope.getKey = (node) ->
|
||||
d = $q.defer()
|
||||
if !node.data
|
||||
$scope.waiting = true
|
||||
if node.get and typeof(node.get) == 'object'
|
||||
node.data = []
|
||||
tmp = []
|
||||
for n, i in node.get
|
||||
node.data[i] =
|
||||
title: n
|
||||
id: n
|
||||
tmp.push $scope.getKey(node.data[i])
|
||||
$q.all(tmp).then ->
|
||||
d.resolve(node.data)
|
||||
,(response) ->
|
||||
d.reject response.statusLine
|
||||
$scope.waiting = false
|
||||
else
|
||||
$http.get("#{window.viewPrefix}#{$scope.currentCfg.cfgNum}/#{if node.get then node.get else node.title}").then (response) ->
|
||||
# Set default value if response is null or if asked by server
|
||||
data = response.data
|
||||
if (data.value == null or (data.error and data.error.match /setDefault$/ ) ) and node['default'] != null
|
||||
node.data = node['default']
|
||||
else
|
||||
node.data = data.value
|
||||
# Cast int as int (remember that booleans are int for Perl)
|
||||
if node.type and node.type.match /^int$/
|
||||
node.data = parseInt(node.data, 10)
|
||||
# Split SAML types
|
||||
else if node.type and node.type.match(/^(saml(Service|Assertion)|blackWhiteList)$/) and not (typeof node.data == 'object')
|
||||
node.data = node.data.split ';'
|
||||
$scope.waiting = false
|
||||
d.resolve node.data
|
||||
, (response) ->
|
||||
readError response
|
||||
d.reject response.status
|
||||
else
|
||||
d.resolve node.data
|
||||
return d.promise
|
||||
|
||||
# function `pathEvent(event, next; current)`:
|
||||
# Called when $location.path() change, launch getCfg() with the new
|
||||
# configuration number
|
||||
pathEvent = (event, next, current) ->
|
||||
n = next.match(new RegExp('#!?/view/(latest|[0-9]+)'))
|
||||
if n == null
|
||||
$location.path '/view/latest'
|
||||
else
|
||||
console.log "Trying to get cfg number #{n[1]}"
|
||||
$scope.getCfg n[1]
|
||||
$scope.$on '$locationChangeSuccess', pathEvent
|
||||
|
||||
# function `getCfg(n)`:
|
||||
# Download configuration metadatas
|
||||
$scope.getCfg = (n) ->
|
||||
if $scope.currentCfg.cfgNum != n
|
||||
$http.get("#{window.viewPrefix}#{n}").then (response) ->
|
||||
$scope.currentCfg = response.data
|
||||
d = new Date $scope.currentCfg.cfgDate * 1000
|
||||
$scope.currentCfg.date = d.toLocaleString()
|
||||
console.log "Metadatas of cfg #{n} loaded"
|
||||
$location.path "/view/#{n}"
|
||||
$scope.init()
|
||||
, (response) ->
|
||||
readError(response).then ->
|
||||
$scope.currentCfg.cfgNum = 0
|
||||
$scope.init()
|
||||
else
|
||||
$scope.waiting = false
|
||||
|
||||
# method `getLanguage(lang)`
|
||||
# Launch init() after setting current language
|
||||
$scope.getLanguage = (lang) ->
|
||||
$scope.lang = lang
|
||||
# Force reload home
|
||||
$scope.form = 'white'
|
||||
$scope.init()
|
||||
$scope.showM = false
|
||||
|
||||
# Initialization
|
||||
|
||||
# Load JSON files:
|
||||
# - struct.json: the main tree
|
||||
# - languages/<lang>.json: the chosen language datas
|
||||
$scope.init = ->
|
||||
tmp = null
|
||||
$scope.waiting = true
|
||||
$scope.data = []
|
||||
$scope.confirmNeeded = false
|
||||
$scope.forceSave = false
|
||||
$q.all [
|
||||
$translator.init($scope.lang),
|
||||
$http.get("#{window.staticPrefix}struct.json").then (response) ->
|
||||
tmp = response.data
|
||||
console.log("Structure loaded")
|
||||
]
|
||||
.then ->
|
||||
console.log("Starting structure binding")
|
||||
$scope.data = tmp
|
||||
tmp = null
|
||||
if $scope.currentCfg.cfgNum != 0
|
||||
setScopeVars $scope
|
||||
else
|
||||
$scope.message =
|
||||
title: 'emptyConf'
|
||||
message: '__zeroConfExplanations__'
|
||||
$scope.showModal 'message.html'
|
||||
$scope.form = 'home'
|
||||
$scope.waiting = false
|
||||
, readError
|
||||
# Colorized link
|
||||
$scope.activeModule = "viewer"
|
||||
$scope.myStyle = {color: '#ffb84d'}
|
||||
|
||||
c = $location.path().match(new RegExp('^/view/(latest|[0-9]+)'))
|
||||
unless c
|
||||
console.log "Redirecting to /view/latest"
|
||||
$location.path '/view/latest'
|
||||
|
||||
]
|
|
@ -1,10 +1,14 @@
|
|||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<TMPL_IF NAME="ALLOWDIFF">
|
||||
<h3 class="panel-title">
|
||||
|
||||
<span ng-if="!currentCfg.next" trspan="currentConfiguration"></span>
|
||||
<span ng-if="currentCfg.next" trspan="loadedConfiguration"></span>
|
||||
<i ng-if="currentCfg.prev">(<a trspan="diffWithPrevious" target="_blank" href="{{scriptname}}/diff.html#!/{{currentCfg.cfgNum}}" role="link"></a>)</i>
|
||||
|
||||
</h3>
|
||||
</TMPL_IF>
|
||||
</div>
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
|
|
|
@ -0,0 +1,539 @@
|
|||
// Generated by CoffeeScript 1.12.7
|
||||
|
||||
/*
|
||||
LemonLDAP::NG Viewer client
|
||||
|
||||
This is the main app file. Other are:
|
||||
- struct.json and js/confTree.js that contains the full tree
|
||||
- translate.json that contains the keywords translation
|
||||
|
||||
This file contains:
|
||||
- the AngularJS controller
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var llapp;
|
||||
|
||||
llapp = angular.module('llngManager', ['ui.tree', 'ui.bootstrap', 'llApp', 'ngCookies']);
|
||||
|
||||
|
||||
/*
|
||||
Main AngularJS controller
|
||||
*/
|
||||
|
||||
llapp.controller('TreeCtrl', [
|
||||
'$scope', '$http', '$location', '$q', '$uibModal', '$translator', '$cookies', '$htmlParams', function($scope, $http, $location, $q, $uibModal, $translator, $cookies, $htmlParams) {
|
||||
var _download, _getAll, _stoggle, c, id, pathEvent, readError, setHelp;
|
||||
$scope.links = window.links;
|
||||
$scope.menu = $htmlParams.menu;
|
||||
$scope.menulinks = window.menulinks;
|
||||
$scope.staticPrefix = window.staticPrefix;
|
||||
$scope.formPrefix = window.formPrefix;
|
||||
$scope.availableLanguages = window.availableLanguages;
|
||||
$scope.waiting = true;
|
||||
$scope.showM = false;
|
||||
$scope.showT = false;
|
||||
$scope.form = 'home';
|
||||
$scope.currentCfg = {};
|
||||
$scope.viewPrefix = window.viewPrefix;
|
||||
$scope.message = {};
|
||||
$scope.result = '';
|
||||
$scope.translateTitle = function(node) {
|
||||
return $translator.translateField(node, 'title');
|
||||
};
|
||||
$scope.translateP = $translator.translateP;
|
||||
$scope.translate = $translator.translate;
|
||||
$scope.helpUrl = 'start.html#configuration';
|
||||
$scope.setShowHelp = function(val) {
|
||||
var d;
|
||||
if (val == null) {
|
||||
val = !$scope.showH;
|
||||
}
|
||||
$scope.showH = val;
|
||||
d = new Date(Date.now());
|
||||
d.setFullYear(d.getFullYear() + 1);
|
||||
return $cookies.put('showhelp', (val ? 'true' : 'false'), {
|
||||
"expires": d
|
||||
});
|
||||
};
|
||||
$scope.showH = $cookies.get('showhelp') === 'false' ? false : true;
|
||||
if ($scope.showH == null) {
|
||||
$scope.setShowHelp(true);
|
||||
}
|
||||
readError = function(response) {
|
||||
var e, j;
|
||||
e = response.status;
|
||||
j = response.statusLine;
|
||||
$scope.waiting = false;
|
||||
if (e === 403) {
|
||||
$scope.message = {
|
||||
title: 'forbidden',
|
||||
message: '',
|
||||
items: []
|
||||
};
|
||||
} else if (e === 401) {
|
||||
console.log('Authentication needed');
|
||||
$scope.message = {
|
||||
title: 'authenticationNeeded',
|
||||
message: '__waitOrF5__',
|
||||
items: []
|
||||
};
|
||||
} else if (e === 400) {
|
||||
$scope.message = {
|
||||
title: 'badRequest',
|
||||
message: j,
|
||||
items: []
|
||||
};
|
||||
} else if (e > 0) {
|
||||
$scope.message = {
|
||||
title: 'badRequest',
|
||||
message: j,
|
||||
items: []
|
||||
};
|
||||
} else {
|
||||
$scope.message = {
|
||||
title: 'networkProblem',
|
||||
message: '',
|
||||
items: []
|
||||
};
|
||||
}
|
||||
return $scope.showModal('message.html');
|
||||
};
|
||||
$scope.showModal = function(tpl, init) {
|
||||
var d, modalInstance;
|
||||
modalInstance = $uibModal.open({
|
||||
templateUrl: tpl,
|
||||
controller: 'ModalInstanceCtrl',
|
||||
size: 'lg',
|
||||
resolve: {
|
||||
elem: function() {
|
||||
return function(s) {
|
||||
return $scope[s];
|
||||
};
|
||||
},
|
||||
set: function() {
|
||||
return function(f, s) {
|
||||
return $scope[f] = s;
|
||||
};
|
||||
},
|
||||
init: function() {
|
||||
return init;
|
||||
}
|
||||
}
|
||||
});
|
||||
d = $q.defer();
|
||||
modalInstance.result.then(function(msgok) {
|
||||
$scope.message = {
|
||||
title: '',
|
||||
message: '',
|
||||
items: []
|
||||
};
|
||||
return d.resolve(msgok);
|
||||
}, function(msgnok) {
|
||||
$scope.message = {
|
||||
title: '',
|
||||
message: '',
|
||||
items: []
|
||||
};
|
||||
return d.reject(msgnok);
|
||||
});
|
||||
return d.promise;
|
||||
};
|
||||
$scope.menuClick = function(button) {
|
||||
if (button.popup) {
|
||||
window.open(button.popup);
|
||||
} else {
|
||||
if (!button.action) {
|
||||
button.action = button.title;
|
||||
}
|
||||
switch (typeof button.action) {
|
||||
case 'function':
|
||||
button.action($scope.currentNode, $scope);
|
||||
break;
|
||||
case 'string':
|
||||
$scope[button.action]();
|
||||
break;
|
||||
default:
|
||||
console.log(typeof button.action);
|
||||
}
|
||||
}
|
||||
return $scope.showM = false;
|
||||
};
|
||||
$scope.home = function() {
|
||||
$scope.form = 'home';
|
||||
return $scope.showM = false;
|
||||
};
|
||||
$scope.downloadConf = function() {
|
||||
return window.open($scope.viewPrefix + $scope.currentCfg.cfgNum + '?full=1');
|
||||
};
|
||||
id = 1;
|
||||
$scope._findContainer = function() {
|
||||
return $scope._findScopeContainer().$modelValue;
|
||||
};
|
||||
$scope._findScopeContainer = function() {
|
||||
var cs;
|
||||
cs = $scope.currentScope;
|
||||
while (!cs.$modelValue.type.match(/Container$/)) {
|
||||
cs = cs.$parentNodeScope;
|
||||
}
|
||||
return cs;
|
||||
};
|
||||
$scope._findScopeByKey = function(k) {
|
||||
var cs;
|
||||
cs = $scope.currentScope;
|
||||
while (!(cs.$modelValue.title === k)) {
|
||||
cs = cs.$parentNodeScope;
|
||||
}
|
||||
return cs;
|
||||
};
|
||||
_getAll = function(node) {
|
||||
var d, d2;
|
||||
d = $q.defer();
|
||||
d2 = $q.defer();
|
||||
if (node._nodes) {
|
||||
_stoggle(node);
|
||||
d.resolve();
|
||||
} else if (node.cnodes) {
|
||||
_download(node).then(function() {
|
||||
return d.resolve();
|
||||
});
|
||||
} else if (node.nodes || node.data) {
|
||||
d.resolve();
|
||||
} else {
|
||||
$scope.getKey(node).then(function() {
|
||||
return d.resolve();
|
||||
});
|
||||
}
|
||||
d.promise.then(function() {
|
||||
var l, len, n, ref, t;
|
||||
t = [];
|
||||
if (node.nodes) {
|
||||
ref = node.nodes;
|
||||
for (l = 0, len = ref.length; l < len; l++) {
|
||||
n = ref[l];
|
||||
t.push(_getAll(n));
|
||||
}
|
||||
}
|
||||
return $q.all(t).then(function() {
|
||||
return d2.resolve();
|
||||
});
|
||||
});
|
||||
return d2.promise;
|
||||
};
|
||||
$scope.down = function() {
|
||||
var i, ind, l, len, n, p, ref, tmp;
|
||||
id = $scope.currentNode.id;
|
||||
p = $scope.currentScope.$parentNodeScope.$modelValue;
|
||||
ind = p.nodes.length;
|
||||
ref = p.nodes;
|
||||
for (i = l = 0, len = ref.length; l < len; i = ++l) {
|
||||
n = ref[i];
|
||||
if (n.id === id) {
|
||||
ind = i;
|
||||
}
|
||||
}
|
||||
if (ind < p.nodes.length - 1) {
|
||||
tmp = p.nodes[ind];
|
||||
p.nodes[ind] = p.nodes[ind + 1];
|
||||
p.nodes[ind + 1] = tmp;
|
||||
}
|
||||
return ind;
|
||||
};
|
||||
$scope.up = function() {
|
||||
var i, ind, l, len, n, p, ref, tmp;
|
||||
id = $scope.currentNode.id;
|
||||
p = $scope.currentScope.$parentNodeScope.$modelValue;
|
||||
ind = -1;
|
||||
ref = p.nodes;
|
||||
for (i = l = 0, len = ref.length; l < len; i = ++l) {
|
||||
n = ref[i];
|
||||
if (n.id === id) {
|
||||
ind = i;
|
||||
}
|
||||
}
|
||||
if (ind > 0) {
|
||||
tmp = p.nodes[ind];
|
||||
p.nodes[ind] = p.nodes[ind - 1];
|
||||
p.nodes[ind - 1] = tmp;
|
||||
}
|
||||
return ind;
|
||||
};
|
||||
$scope.inSelect = function(value) {
|
||||
var l, len, n, ref;
|
||||
ref = $scope.currentNode.select;
|
||||
for (l = 0, len = ref.length; l < len; l++) {
|
||||
n = ref[l];
|
||||
if (n.k === value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
$scope.changeRuleTitle = function(node) {
|
||||
return node.title = node.comment.length > 0 ? node.comment : node.re;
|
||||
};
|
||||
$scope.filters = {};
|
||||
$scope.execFilters = function(scope) {
|
||||
var filter, func, ref;
|
||||
scope = scope ? scope : $scope;
|
||||
ref = $scope.filters;
|
||||
for (filter in ref) {
|
||||
func = ref[filter];
|
||||
if ($scope.filters.hasOwnProperty(filter)) {
|
||||
return window.filterFunctions[filter](scope, $q, func);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
$scope.stoggle = function(scope) {
|
||||
var node;
|
||||
node = scope.$modelValue;
|
||||
_stoggle(node);
|
||||
return scope.toggle();
|
||||
};
|
||||
_stoggle = function(node) {
|
||||
var a, l, len, len1, len2, m, n, o, ref, ref1, ref2;
|
||||
ref = ['nodes', 'nodes_cond'];
|
||||
for (l = 0, len = ref.length; l < len; l++) {
|
||||
n = ref[l];
|
||||
if (node["_" + n]) {
|
||||
node[n] = [];
|
||||
ref1 = node["_" + n];
|
||||
for (m = 0, len1 = ref1.length; m < len1; m++) {
|
||||
a = ref1[m];
|
||||
node[n].push(a);
|
||||
}
|
||||
delete node["_" + n];
|
||||
}
|
||||
}
|
||||
if (node._nodes_filter) {
|
||||
if (node.nodes) {
|
||||
ref2 = node.nodes;
|
||||
for (o = 0, len2 = ref2.length; o < len2; o++) {
|
||||
n = ref2[o];
|
||||
n.onChange = $scope.execFilters;
|
||||
}
|
||||
}
|
||||
$scope.filters[node._nodes_filter] = node;
|
||||
return $scope.execFilters();
|
||||
}
|
||||
};
|
||||
$scope.toggle = function(scope) {
|
||||
return scope.toggle();
|
||||
};
|
||||
$scope.download = function(scope) {
|
||||
var node;
|
||||
node = scope.$modelValue;
|
||||
return _download(node);
|
||||
};
|
||||
_download = function(node) {
|
||||
var d;
|
||||
d = $q.defer();
|
||||
d.notify('Trying to get datas');
|
||||
$scope.waiting = true;
|
||||
$http.get("" + window.viewPrefix + $scope.currentCfg.cfgNum + "/" + node.cnodes).then(function(response) {
|
||||
var a, data, l, len;
|
||||
data = response.data;
|
||||
if (!data) {
|
||||
d.reject('Empty response from server');
|
||||
} else if (data.error) {
|
||||
if (data.error.match(/setDefault$/)) {
|
||||
if (node['default']) {
|
||||
node.nodes = node['default'].slice(0);
|
||||
} else {
|
||||
node.nodes = [];
|
||||
}
|
||||
delete node.cnodes;
|
||||
d.resolve('Set data to default value');
|
||||
} else {
|
||||
d.reject("Server return an error: " + data.error);
|
||||
}
|
||||
} else {
|
||||
delete node.cnodes;
|
||||
if (!node.type) {
|
||||
node.type = 'keyTextContainer';
|
||||
}
|
||||
node.nodes = [];
|
||||
for (l = 0, len = data.length; l < len; l++) {
|
||||
a = data[l];
|
||||
if (a.template) {
|
||||
a._nodes = templates(a.template, a.title);
|
||||
}
|
||||
node.nodes.push(a);
|
||||
}
|
||||
d.resolve('OK');
|
||||
}
|
||||
return $scope.waiting = false;
|
||||
}, function(response) {
|
||||
readError(response);
|
||||
return d.reject('');
|
||||
});
|
||||
return d.promise;
|
||||
};
|
||||
$scope.openCnode = function(scope) {
|
||||
return $scope.download(scope).then(function() {
|
||||
return scope.toggle();
|
||||
});
|
||||
};
|
||||
setHelp = function(scope) {
|
||||
while (!scope.$modelValue.help && scope.$parentNodeScope) {
|
||||
scope = scope.$parentNodeScope;
|
||||
}
|
||||
return $scope.helpUrl = scope.$modelValue.help || 'start.html#configuration';
|
||||
};
|
||||
$scope.displayForm = function(scope) {
|
||||
var f, l, len, n, node, ref;
|
||||
node = scope.$modelValue;
|
||||
if (node.cnodes) {
|
||||
$scope.download(scope);
|
||||
}
|
||||
if (node._nodes) {
|
||||
$scope.stoggle(scope);
|
||||
}
|
||||
$scope.currentNode = node;
|
||||
$scope.currentScope = scope;
|
||||
f = node.type ? node.type : 'text';
|
||||
if (node.nodes || node._nodes || node.cnodes) {
|
||||
$scope.form = f !== 'text' ? f : 'mini';
|
||||
} else {
|
||||
$scope.form = f;
|
||||
$scope.getKey(node);
|
||||
}
|
||||
if (node.type && node.type === 'simpleInputContainer') {
|
||||
ref = node.nodes;
|
||||
for (l = 0, len = ref.length; l < len; l++) {
|
||||
n = ref[l];
|
||||
$scope.getKey(n);
|
||||
}
|
||||
}
|
||||
$scope.showT = false;
|
||||
return setHelp(scope);
|
||||
};
|
||||
$scope.getKey = function(node) {
|
||||
var d, i, l, len, n, ref, tmp;
|
||||
d = $q.defer();
|
||||
if (!node.data) {
|
||||
$scope.waiting = true;
|
||||
if (node.get && typeof node.get === 'object') {
|
||||
node.data = [];
|
||||
tmp = [];
|
||||
ref = node.get;
|
||||
for (i = l = 0, len = ref.length; l < len; i = ++l) {
|
||||
n = ref[i];
|
||||
node.data[i] = {
|
||||
title: n,
|
||||
id: n
|
||||
};
|
||||
tmp.push($scope.getKey(node.data[i]));
|
||||
}
|
||||
$q.all(tmp).then(function() {
|
||||
return d.resolve(node.data);
|
||||
}, function(response) {
|
||||
d.reject(response.statusLine);
|
||||
return $scope.waiting = false;
|
||||
});
|
||||
} else {
|
||||
$http.get("" + window.viewPrefix + $scope.currentCfg.cfgNum + "/" + (node.get ? node.get : node.title)).then(function(response) {
|
||||
var data;
|
||||
data = response.data;
|
||||
if ((data.value === null || (data.error && data.error.match(/setDefault$/))) && node['default'] !== null) {
|
||||
node.data = node['default'];
|
||||
} else {
|
||||
node.data = data.value;
|
||||
}
|
||||
if (node.type && node.type.match(/^int$/)) {
|
||||
node.data = parseInt(node.data, 10);
|
||||
} else if (node.type && node.type.match(/^(saml(Service|Assertion)|blackWhiteList)$/) && !(typeof node.data === 'object')) {
|
||||
node.data = node.data.split(';');
|
||||
}
|
||||
$scope.waiting = false;
|
||||
return d.resolve(node.data);
|
||||
}, function(response) {
|
||||
readError(response);
|
||||
return d.reject(response.status);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
d.resolve(node.data);
|
||||
}
|
||||
return d.promise;
|
||||
};
|
||||
pathEvent = function(event, next, current) {
|
||||
var n;
|
||||
n = next.match(new RegExp('#!?/view/(latest|[0-9]+)'));
|
||||
if (n === null) {
|
||||
return $location.path('/view/latest');
|
||||
} else {
|
||||
console.log("Trying to get cfg number " + n[1]);
|
||||
return $scope.getCfg(n[1]);
|
||||
}
|
||||
};
|
||||
$scope.$on('$locationChangeSuccess', pathEvent);
|
||||
$scope.getCfg = function(n) {
|
||||
if ($scope.currentCfg.cfgNum !== n) {
|
||||
return $http.get("" + window.viewPrefix + n).then(function(response) {
|
||||
var d;
|
||||
$scope.currentCfg = response.data;
|
||||
d = new Date($scope.currentCfg.cfgDate * 1000);
|
||||
$scope.currentCfg.date = d.toLocaleString();
|
||||
console.log("Metadatas of cfg " + n + " loaded");
|
||||
$location.path("/view/" + n);
|
||||
return $scope.init();
|
||||
}, function(response) {
|
||||
return readError(response).then(function() {
|
||||
$scope.currentCfg.cfgNum = 0;
|
||||
return $scope.init();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return $scope.waiting = false;
|
||||
}
|
||||
};
|
||||
$scope.getLanguage = function(lang) {
|
||||
$scope.lang = lang;
|
||||
$scope.form = 'white';
|
||||
$scope.init();
|
||||
return $scope.showM = false;
|
||||
};
|
||||
$scope.init = function() {
|
||||
var tmp;
|
||||
tmp = null;
|
||||
$scope.waiting = true;
|
||||
$scope.data = [];
|
||||
$scope.confirmNeeded = false;
|
||||
$scope.forceSave = false;
|
||||
$q.all([
|
||||
$translator.init($scope.lang), $http.get(window.staticPrefix + "struct.json").then(function(response) {
|
||||
tmp = response.data;
|
||||
return console.log("Structure loaded");
|
||||
})
|
||||
]).then(function() {
|
||||
console.log("Starting structure binding");
|
||||
$scope.data = tmp;
|
||||
tmp = null;
|
||||
if ($scope.currentCfg.cfgNum !== 0) {
|
||||
setScopeVars($scope);
|
||||
} else {
|
||||
$scope.message = {
|
||||
title: 'emptyConf',
|
||||
message: '__zeroConfExplanations__'
|
||||
};
|
||||
$scope.showModal('message.html');
|
||||
}
|
||||
$scope.form = 'home';
|
||||
return $scope.waiting = false;
|
||||
}, readError);
|
||||
$scope.activeModule = "viewer";
|
||||
return $scope.myStyle = {
|
||||
color: '#ffb84d'
|
||||
};
|
||||
};
|
||||
c = $location.path().match(new RegExp('^/view/(latest|[0-9]+)'));
|
||||
if (!c) {
|
||||
console.log("Redirecting to /view/latest");
|
||||
return $location.path('/view/latest');
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
}).call(this);
|
File diff suppressed because one or more lines are too long
|
@ -648,6 +648,7 @@
|
|||
"radiusSecret":"سر مشترك",
|
||||
"radiusServer":"اسم الخادم",
|
||||
"randomPasswordRegexp":"التعبير النمطي لتوليد كلمة المرور",
|
||||
"readOnlyMode":"Read-Only mode",
|
||||
"redirectFormMethod":"طريقة إعادة توجيه الإستمارة",
|
||||
"redirection":"معالج إعادة التوجيه",
|
||||
"reference":"مرجع",
|
||||
|
@ -819,6 +820,7 @@
|
|||
"vhostPort":"المنفذ",
|
||||
"vhostType":"نوع",
|
||||
"view":"عرض",
|
||||
"viewer":"Viewer",
|
||||
"virtualHost":"المضيف الإفتراضى ",
|
||||
"virtualHostName":"اسم المضيف الافتراضي",
|
||||
"virtualHosts":"المضيفين الإفتراضيين",
|
||||
|
|
|
@ -648,6 +648,7 @@
|
|||
"radiusSecret":"Shared secret",
|
||||
"radiusServer":"Server hostname",
|
||||
"randomPasswordRegexp":"Regexp for password generation",
|
||||
"readOnlyMode":"Read-Only mode",
|
||||
"redirectFormMethod":"Method for redirect form",
|
||||
"redirection":"Handler redirections",
|
||||
"reference":"Reference",
|
||||
|
@ -819,6 +820,7 @@
|
|||
"vhostPort":"Port",
|
||||
"vhostType":"Type",
|
||||
"view":"View",
|
||||
"viewer":"Viewer",
|
||||
"virtualHost":"Virtual Host",
|
||||
"virtualHostName":"Virtual host hostname",
|
||||
"virtualHosts":"Virtual Hosts",
|
||||
|
|
|
@ -648,6 +648,7 @@
|
|||
"radiusSecret":"Shared secret",
|
||||
"radiusServer":"Server hostname",
|
||||
"randomPasswordRegexp":"Regexp for password generation",
|
||||
"readOnlyMode":"Read-Only mode",
|
||||
"redirectFormMethod":"Method for redirect form",
|
||||
"redirection":"Handler redirections",
|
||||
"reference":"Reference",
|
||||
|
@ -819,6 +820,7 @@
|
|||
"vhostPort":"Port",
|
||||
"vhostType":"Type",
|
||||
"view":"View",
|
||||
"viewer":"Viewer",
|
||||
"virtualHost":"Virtual Host",
|
||||
"virtualHostName":"Virtual host hostname",
|
||||
"virtualHosts":"Virtual Hosts",
|
||||
|
|
|
@ -648,6 +648,7 @@
|
|||
"radiusSecret":"Secret partagé",
|
||||
"radiusServer":"Nom d'hôte du serveur",
|
||||
"randomPasswordRegexp":"Expression regulière pour la génération des mots de passe",
|
||||
"readOnlyMode":"Mode lecture seule",
|
||||
"redirectFormMethod":"Méthode du formulaire de redirection",
|
||||
"redirection":"Redirections du Handler",
|
||||
"reference":"Référence",
|
||||
|
@ -819,6 +820,7 @@
|
|||
"vhostPort":"Port",
|
||||
"vhostType":"Type",
|
||||
"view":"Aperçu",
|
||||
"viewer":"Explorateur",
|
||||
"virtualHost":"Hôte virtuel",
|
||||
"virtualHostName":"Nom de l'hôte virtuel",
|
||||
"virtualHosts":"Hôtes virtuels",
|
||||
|
|
|
@ -648,6 +648,7 @@
|
|||
"radiusSecret":"Segreto condiviso",
|
||||
"radiusServer":"Nome host del server",
|
||||
"randomPasswordRegexp":"Regex per la generazione di password",
|
||||
"readOnlyMode":"Read-Only mode",
|
||||
"redirectFormMethod":"Metodo per il modulo di reindirizzamento",
|
||||
"redirection":"Redirezioni del gestore",
|
||||
"reference":"Riferimento",
|
||||
|
@ -819,6 +820,7 @@
|
|||
"vhostPort":"Porta",
|
||||
"vhostType":"Typo",
|
||||
"view":"Visualizzazione",
|
||||
"viewer":"Viewer",
|
||||
"virtualHost":"Virtual Host",
|
||||
"virtualHostName":"Hostname di Virtual host",
|
||||
"virtualHosts":"Virtual Hosts",
|
||||
|
|
|
@ -648,6 +648,7 @@
|
|||
"radiusSecret":"Bí mật đã được chia sẻ",
|
||||
"radiusServer":"Máy chủ lưu trữ",
|
||||
"randomPasswordRegexp":"Regexp để tạo mật khẩu",
|
||||
"readOnlyMode":"Read-Only mode",
|
||||
"redirectFormMethod":"Phương pháp chuyển hướng mẫu",
|
||||
"redirection":"chuyển hướng trình điều khiển",
|
||||
"reference":"Tham khảo",
|
||||
|
@ -819,6 +820,7 @@
|
|||
"vhostPort":"Port",
|
||||
"vhostType":"Loại",
|
||||
"view":"Khung nhìn",
|
||||
"viewer":"Viewer",
|
||||
"virtualHost":"Máy chủ ảo",
|
||||
"virtualHostName":"Tên máy chủ lưu trữ ảo",
|
||||
"virtualHosts":"Máy chủ ảo",
|
||||
|
|
|
@ -648,6 +648,7 @@
|
|||
"radiusSecret":"Shared secret",
|
||||
"radiusServer":"Server hostname",
|
||||
"randomPasswordRegexp":"Regexp for password generation",
|
||||
"readOnlyMode":"Read-Only mode",
|
||||
"redirectFormMethod":"Method for redirect form",
|
||||
"redirection":"Handler redirections",
|
||||
"reference":"Reference",
|
||||
|
@ -819,6 +820,7 @@
|
|||
"vhostPort":"Port",
|
||||
"vhostType":"Type",
|
||||
"view":"View",
|
||||
"viewer":"Viewer",
|
||||
"virtualHost":"Virtual Host",
|
||||
"virtualHostName":"Virtual host hostname",
|
||||
"virtualHosts":"Virtual Hosts",
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!-- Last buttons, available languages -->
|
||||
</div>
|
||||
<ul class="hidden-xs nav navbar-nav" role="grid">
|
||||
<li ng-repeat="l in links" id="l in links"><a href="{{l.target}}" role="row" ng-mousedown="clickStyle={color: '#ffb84d'}"><strong><i ng-if="activeModule == l.title" ng-style="myStyle" class="glyphicon glyphicon-{{l.icon}}"></i><i ng-if="activeModule != l.title" class="glyphicon glyphicon-{{l.icon}}" ng-style="clickStyle"></i> <span ng-if="activeModule == l.title" ng-style="myStyle" ng-bind="translate(l.title)"></span><span ng-if="activeModule != l.title" ng-bind="translate(l.title)" ng-style="clickStyle"></span></strong></a></li>
|
||||
<li ng-repeat="l in links" id="l in links"><a href="{{l.target}}" role="row" ng-mouseup="clickStyle={color: '#ffb84d'}"><strong><i ng-if="activeModule == l.title" ng-style="myStyle" class="glyphicon glyphicon-{{l.icon}}"></i><i ng-if="activeModule != l.title" class="glyphicon glyphicon-{{l.icon}}" ng-style="clickStyle"></i> <span ng-if="activeModule == l.title" ng-style="myStyle" ng-bind="translate(l.title)"></span><span ng-if="activeModule != l.title" ng-bind="translate(l.title)" ng-style="clickStyle"></span></strong></a></li>
|
||||
</ul>
|
||||
<ul class="hidden-xs nav navbar-nav navbar-right">
|
||||
<li uib-dropdown>
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
<TMPL_INCLUDE NAME="header.tpl">
|
||||
|
||||
<title>LemonLDAP::NG Manager</title>
|
||||
<link rel="prefetch" href="<TMPL_VAR NAME="STATIC_PREFIX">forms/home.html" />
|
||||
<link rel="prefetch" href="<TMPL_VAR NAME="STATIC_PREFIX">struct.json" />
|
||||
</head>
|
||||
|
||||
<body ng-app="llngManager" ng-controller="TreeCtrl" ng-csp>
|
||||
|
||||
<TMPL_INCLUDE NAME="menubar.tpl">
|
||||
|
||||
<div id="content" class="row container-fluid">
|
||||
|
||||
<TMPL_INCLUDE NAME="tree.tpl">
|
||||
|
||||
<!-- Right(main) div -->
|
||||
<div id="right" class="col-lg-8 col-md-8 col-sm-7 col-xs-12 scrollable" ng-class="{'hidden-xs':showT&&!showM}">
|
||||
<!-- Form container -->
|
||||
<div id="top">
|
||||
<!-- Menu buttons -->
|
||||
<div class="lmmenu navbar navbar-default" ng-class="{'hidden-xs':!showM}">
|
||||
<div class="navbar-collapse" ng-class="{'collapse':!showM}" id="formmenu">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a class="link" ng-click="home()"><i class="glyphicon glyphicon-home"></i></a></li>
|
||||
|
||||
<TMPL_IF NAME="ALLOWBROWSER">
|
||||
<li uib-dropdown>
|
||||
<a id="navmenu" name="menu" uib-dropdown-toggle data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><i class="glyphicon glyphicon-cog"></i> {{translate('browse')}} <span class="caret"></span></a>
|
||||
<ul uib-dropdown-menu aria-labelled-by="navmenu">
|
||||
<li ng-class="{'disabled':!currentCfg.prev}"><a class="link" ng-click="currentCfg.prev && getCfg(currentCfg.prev)" title="Configuration {{currentCfg.prev}}"><i class="glyphicon glyphicon-arrow-left"></i> {{translate('previous')}}</a></li>
|
||||
<li ng-class="{'disabled':!currentCfg.next}"><a class="link" ng-click="currentCfg.next && getCfg(currentCfg.next)" title="Configuration {{currentCfg.next}}"><i class="glyphicon glyphicon-arrow-right"></i> {{translate('next')}}</a></a></li>
|
||||
<li><a class="link" ng-click="getCfg('latest')" title="Latest configuration"><i class="glyphicon glyphicon-refresh"></i> {{translate('latest')}}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</TMPL_IF>
|
||||
|
||||
<li><a class="link hidden-xs" ng-click="setShowHelp()"><i class="glyphicon" ng-class="{'glyphicon-eye-close': showH,'glyphicon-eye-open': !showH}" ></i> {{ translate((showH ? 'hideHelp' : 'showHelp')) }}</a></li>
|
||||
<li uib-dropdown class="visible-xs">
|
||||
<a id="langmenu" name="menu" uib-dropdown-toggle data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{translate('menu')}} <span class="caret"></span></a>
|
||||
<ul uib-dropdown-menu aria-labelled-by="langmenu" role="grid">
|
||||
<li ng-repeat="link in links"><a href="{{link.target}}" role="row"><i ng-if="link.icon" class="glyphicon glyphicon-{{link.icon}}"></i> {{translate(link.title)}}</a></li>
|
||||
<li ng-repeat="menulink in menulinks"><a href="{{menulink.target}}" role="row"><i ng-if="menulink.icon" class="glyphicon glyphicon-{{menulink.icon}}"></i> {{translate(menulink.title)}}</a></li>
|
||||
<li ng-include="'languages.html'"></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<i class="glyphicon glyphicon-lock"></i>
|
||||
<u>{{translate('readOnlyMode')}}</u>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<form class="form-group slide-animate-container" ng-include="formPrefix+form+'.html'" scope="$scope" />
|
||||
</div>
|
||||
<!-- Help container -->
|
||||
<div id="bottom" ng-if="showH" class="hidden-xs">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<iframe id="helpframe" width="100%" height="100%" ng-src="{{translate('/doc/')+'pages/documentation/current/'+helpUrl}}" frameborder="0"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HTML recursive templates (used in `ng-repeat... ng-include="'template.html'") -->
|
||||
<!-- Tree nested node template -->
|
||||
<script type="text/ng-template" id="nodes_renderer.html">
|
||||
<div ui-tree-handle class="tree-node panel-info" ng-class="{'bg-info':this.$modelValue===currentNode,'tree-node-default':this.$modelValue!==currentNode}">
|
||||
<!-- Glyph icons -->
|
||||
<span ng-switch="node.nodes||node.nodes_cond?1:((node._nodes&&node._nodes.length>0)||(node._nodes_cond&&node._nodes_cond.length>0)?3:(node.cnodes&&node.cnodes.length>0?2:0))">
|
||||
<!-- Undownloaded nodes (hash data)-->
|
||||
<a class="btn btn-sm" id="a-{{node.id}}" ng-switch-when="2" ng-click="openCnode(this)">
|
||||
<span class="glyphicon glyphicon-chevron-right"></span>
|
||||
</a>
|
||||
<!-- Javascript nodes not yet bind to DOM -->
|
||||
<a class="btn btn-sm" id="a-{{node.id}}" ng-switch-when="3" ng-click="stoggle(this)">
|
||||
<span class="glyphicon" ng-class="{'glyphicon-chevron-right': collapsed, 'glyphicon-chevron-down': !collapsed}"></span>
|
||||
</a>
|
||||
<!-- Nodes already loaded and binded -->
|
||||
<a class="btn btn-sm" id="a-{{node.id}}" ng-switch-when="1" ng-click="toggle(this)">
|
||||
<span class="glyphicon" ng-class="{'glyphicon-chevron-right': collapsed, 'glyphicon-chevron-down': !collapsed}"></span>
|
||||
</a>
|
||||
<!-- Leaf -->
|
||||
<a class="btn btn-sm" ng-switch-default ng-click="displayForm(this)">
|
||||
<span class="glyphicon glyphicon-pencil"></span>
|
||||
</a>
|
||||
</span>
|
||||
<!-- Node text with/without translation -->
|
||||
<span id="t-{{node.id}}" ng-if="keyWritable(this)" ng-click="displayForm(this)">{{node.title}}</span>
|
||||
<span id="t-{{node.id}}" ng-if="!keyWritable(this)" ng-click="displayForm(this)" trspan="{{node.title}}" />
|
||||
</div>
|
||||
<!-- Subnodes -->
|
||||
<ol ui-tree-nodes="btn btn-sm" ng-model="node.nodes" ng-class="{hidden: collapsed}">
|
||||
<li ng-repeat="node in node.nodes track by node.id" ui-tree-node ng-include="'nodes_renderer.html'" collapsed="true"></li>
|
||||
</ol>
|
||||
<!-- Filtered subnodes (authParams mechanism) -->
|
||||
<ol ui-tree-nodes="btn btn-sm" ng-model="node.nodes_cond" ng-class="{hidden: collapsed}">
|
||||
<li ng-repeat="(name,node) in node.nodes_cond track by node.id" ng-if="node.show" ui-tree-node ng-include="'nodes_renderer.html'" collapsed="true"></li>
|
||||
</ol>
|
||||
</script>
|
||||
|
||||
<!-- Prompt -->
|
||||
<script type="text/ng-template" id="prompt.html">
|
||||
<div role="alertdialog" aria-labelledby="ptitle" aria-describedby="ptitle">
|
||||
<div class="modal-header">
|
||||
<h3 id="ptitle" class="modal-title" trspan="{{elem('message').title}}" />
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="input-group maxw">
|
||||
<label class="input-group-addon" id="promptlabel" for="promptinput" trspan="{{elem('message').field}}"/>
|
||||
<input id="promptinput" class="form-control" ng-model="result" aria-describedby="promptlabel"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" id="promptok" ng-click="ok()" trspan="ok" role="button"></button>
|
||||
<button class="btn btn-warning" ng-click="cancel()" trspan="cancel" role="button"></button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- Message display -->
|
||||
<script type="text/ng-template" id="message.html">
|
||||
<div role="alertdialog" aria-labelledby="mtitle" aria-describedby="mbody">
|
||||
<div class="modal-header">
|
||||
<h3 id="mtitle" class="modal-title" trspan="{{elem('message').title}}" />
|
||||
</div>
|
||||
<div id="mbody" class="modal-body">
|
||||
<div class="modal-p">{{translateP(elem('message').message)}}</div>
|
||||
<ul class="main-modal-ul" ng-model="elem('message').items">
|
||||
<li ng-repeat="item in elem('message').items" ng-include="'messageitem.html'"/>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" id="messageok" ng-click="ok()" trspan="ok" role="button"></button>
|
||||
<button class="btn btn-warning" ng-click="cancel()" ng-if="elem('message').displayCancel" trspan="cancel" role="button"></button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="messageitem.html">
|
||||
<div class="modal-p">{{translateP(item.message)}}</div>
|
||||
<ul class="modal-ul" ng-model="item.items">
|
||||
<li ng-repeat="item in item.items" ng-include="'messageitem.html'"/>
|
||||
</ul>
|
||||
</script>
|
||||
|
||||
<!-- Password question -->
|
||||
<script type="text/ng-template" id="password.html">
|
||||
<div role="alertdialog" aria-labelledby="pwtitle" aria-describedby="pwtitle">
|
||||
<div class="modal-header">
|
||||
<h3 id="pwtitle" class="modal-title" trspan="enterPassword" />
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="input-group maxw">
|
||||
<label class="input-group-addon" id="mlabel" for="mdPwd" trspan="password"/>
|
||||
<input id="mdPwd" class="form-control" ng-model="result" aria-describedby="mlabel"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" id="passwordok" ng-click="ok()" trspan="ok" role="button"></button>
|
||||
<button class="btn btn-warning" ng-click="cancel()" trspan="cancel" role="button"></button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- Save confirm -->
|
||||
<script type="text/ng-template" id="save.html">
|
||||
<div role="alertdialog" aria-labelledby="stitle" aria-describedby="sbody">
|
||||
<div class="modal-header">
|
||||
<h3 id="stitle" class="modal-title" trspan="savingConfirmation" />
|
||||
</div>
|
||||
<div id="sbody" class="modal-body">
|
||||
<div class="input-group maxw">
|
||||
<label id="slabel" class="input-group-addon" for="longtextinput" trspan="cfgLog"/>
|
||||
<textarea id="longtextinput" rows="5" class="form-control" ng-model="result" aria-describedby="slabel" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" id="saveok" ng-click="ok()" trspan="ok" role="button"></button>
|
||||
<button class="btn btn-warning" ng-click="cancel()" trspan="cancel" role="button"></button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<TMPL_INCLUDE NAME="scripts.tpl">
|
||||
|
||||
<!-- //if:jsminified
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/conftree.min.js"></script>
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/filterFunctions.min.js"></script>
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/viewer.min.js"></script>
|
||||
//else -->
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/conftree.js"></script>
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/filterFunctions.js"></script>
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/viewer.js"></script>
|
||||
<!-- //endif -->
|
||||
|
||||
<TMPL_INCLUDE NAME="footer.tpl">
|
|
@ -47,6 +47,8 @@ my @notManagedAttributes = (
|
|||
# Loggers
|
||||
'log4perlConfFile', 'userSyslogFacility', 'logger', 'sentryDsn',
|
||||
'syslogFacility', 'userLogger', 'logLevel',
|
||||
# Viewer
|
||||
'viewerHiddenPK', 'viewerAllowBrowser',
|
||||
|
||||
# Other ini-only prms
|
||||
'configStorage', 'status', 'localStorageOptions', 'localStorage',
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
-I .
|
||||
-I ../lemonldap-ng-common/blib/lib
|
||||
-I ../lemonldap-ng-handler/blib/lib
|
||||
--blib
|
|
@ -352,8 +352,7 @@ sub display {
|
|||
# * Bad URL error
|
||||
elsif ($req->{error} == PE_LOGOUT_OK
|
||||
or $req->{error} == PE_WAIT
|
||||
or $req->{error} == PE_BADURL
|
||||
or $req->{error} == PE_BADCREDENTIALS )
|
||||
or $req->{error} == PE_BADURL )
|
||||
{
|
||||
%templateParams = (
|
||||
%templateParams,
|
||||
|
|
|
@ -210,6 +210,12 @@ sub do {
|
|||
if ( $err == PE_SENDRESPONSE ) {
|
||||
return $req->response;
|
||||
}
|
||||
|
||||
# Remove userData if authentication fails
|
||||
if ( $err == PE_BADCREDENTIALS ) {
|
||||
$req->userData( {} );
|
||||
}
|
||||
|
||||
if ( !$self->conf->{noAjaxHook} and $req->wantJSON ) {
|
||||
$self->logger->debug('Processing to JSON response');
|
||||
if ( ( $err > 0 and !$req->id ) or $err eq PE_SESSIONNOTGRANTED ) {
|
||||
|
|
|
@ -45,7 +45,11 @@ sub run {
|
|||
}
|
||||
|
||||
# Avoid display notification if AuthResult is not null
|
||||
return PE_BADCREDENTIALS if $req->authResult > PE_OK;
|
||||
if ( $req->authResult > PE_OK ) {
|
||||
$self->logger->debug(
|
||||
"Bad authentication, do not check grant session rules");
|
||||
return PE_BADCREDENTIALS;
|
||||
}
|
||||
|
||||
foreach ( sort sortByComment keys %{ $self->rules } ) {
|
||||
$self->logger->debug( "Grant session condition -> "
|
||||
|
|
|
@ -29,8 +29,53 @@ ok( $res->[2]->[0] =~ m%<span id="languages"></span>%, ' Language icons found' )
|
|||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(2);
|
||||
|
||||
# Try to authenticate
|
||||
# -------------------
|
||||
# Try to authenticate with unknown user
|
||||
# -------------------------------------
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=jdoe&password=jdoe'),
|
||||
accept => 'text/html',
|
||||
length => 23
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
count(1);
|
||||
ok(
|
||||
$res->[2]->[0] =~ /<span trmsg="5"><\/span><\/div>/,
|
||||
'jdoe rejected with PE_BADCREDENTIALS'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
ok( $res->[2]->[0] =~ m%<span trspan="connect">Connect</span>%,
|
||||
'Found connect button' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
# Try to authenticate with bad password
|
||||
# -------------------------------------
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=jdoe'),
|
||||
accept => 'text/html',
|
||||
length => 23
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
count(1);
|
||||
ok(
|
||||
$res->[2]->[0] =~ /<span trmsg="5"><\/span><\/div>/,
|
||||
'dwho rejected with PE_BADCREDENTIALS'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
ok( $res->[2]->[0] =~ m%<span trspan="connect">Connect</span>%,
|
||||
'Found connect button' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
|
||||
# Try to authenticate with good password
|
||||
# --------------------------------------
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
|
|
|
@ -54,8 +54,8 @@ ok( $res->[2]->[0] =~ /<span trmsg="5"><\/span><\/div>/,
|
|||
'dwho rejected with PE_BADCREDENTIALS' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
ok( $res->[2]->[0] =~ m%<span trspan="goToPortal">Go to portal</span>%,
|
||||
'Found goToPortal button' )
|
||||
ok( $res->[2]->[0] =~ m%<span trspan="connect">Connect</span>%,
|
||||
'Found connect button' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
|
@ -125,8 +125,8 @@ ok(
|
|||
'rtyler rejected with PE_BADCREDENTIALS'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
ok( $res->[2]->[0] =~ m%<span trspan="goToPortal">Go to portal</span>%,
|
||||
'Found goToPortal button' )
|
||||
ok( $res->[2]->[0] =~ m%<span trspan="connect">Connect</span>%,
|
||||
'Found connect button' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
|
|
Loading…
Reference in New Issue