diff --git a/classes/UserHandler.php b/classes/UserHandler.php index 1963a2223a789b3ab7c9df1751a1b88acc9abbb0..f65c2946173c21302d77df939f6d114f066a6ffa 100644 --- a/classes/UserHandler.php +++ b/classes/UserHandler.php @@ -68,6 +68,10 @@ class UserHandler { $userId1 = $user1->id; $userId2 = $user2->id; + if ($userId1 === $userId2) { + return $user1; + } + // Call Grouper for moving groups and privileges from one user to the other if (isset($this->locator->config->gms)) { diff --git a/classes/login/ShibbolethLogin.php b/classes/login/ShibbolethLogin.php index 072361860a723de3396556b60589eae342f1f47c..faa7c1fb164f3dee711ad48f92f42fc4fd81b718 100644 --- a/classes/login/ShibbolethLogin.php +++ b/classes/login/ShibbolethLogin.php @@ -31,8 +31,7 @@ class ShibbolethLogin extends LoginHandler { $identity->eppn = $eppn; }); } else { - http_response_code(500); - die("Shib-Session-ID not found!"); + throw new ServerErrorException("Shib-Session-ID not found!"); } } diff --git a/css/animation.css b/css/animation.css deleted file mode 100644 index be95362c851d3dd2a72cbbf8936fcd804271a077..0000000000000000000000000000000000000000 --- a/css/animation.css +++ /dev/null @@ -1,151 +0,0 @@ -@charset "UTF-8"; -/* - Animation example, for spinners -*/ -.animate-spin { - -moz-animation: spin 2s infinite linear; - -o-animation: spin 2s infinite linear; - -webkit-animation: spin 2s infinite linear; - animation: spin 2s infinite linear; - display: inline-block; -} -@-moz-keyframes spin { - 0% { - -moz-transform: rotate(0deg); - -o-transform: rotate(0deg); - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -moz-transform: rotate(359deg); - -o-transform: rotate(359deg); - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} -@-webkit-keyframes spin { - 0% { - -moz-transform: rotate(0deg); - -o-transform: rotate(0deg); - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -moz-transform: rotate(359deg); - -o-transform: rotate(359deg); - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} -@-o-keyframes spin { - 0% { - -moz-transform: rotate(0deg); - -o-transform: rotate(0deg); - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -moz-transform: rotate(359deg); - -o-transform: rotate(359deg); - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} -@-ms-keyframes spin { - 0% { - -moz-transform: rotate(0deg); - -o-transform: rotate(0deg); - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -moz-transform: rotate(359deg); - -o-transform: rotate(359deg); - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} -@keyframes spin { - 0% { - -moz-transform: rotate(0deg); - -o-transform: rotate(0deg); - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -moz-transform: rotate(359deg); - -o-transform: rotate(359deg); - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} - -/*! - * animate.css -http://daneden.me/animate - * Version - 3.5.2 - * Licensed under the MIT license - http://opensource.org/licenses/MIT - * - * Copyright (c) 2017 Daniel Eden - */ -.animated { - animation-duration: 1s; - animation-fill-mode: both; -} - -.animated.infinite { - animation-iteration-count: infinite; -} - -.animated.hinge { - animation-duration: 2s; -} - -.animated.flipOutX, -.animated.flipOutY, -.animated.bounceIn, -.animated.bounceOut { - animation-duration: .75s; -} - - -@keyframes bounceIn { - from, 20%, 40%, 60%, 80%, to { - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - transform: scale3d(.3, .3, .3); - } - - 20% { - transform: scale3d(1.1, 1.1, 1.1); - } - - 40% { - transform: scale3d(.9, .9, .9); - } - - 60% { - opacity: 1; - transform: scale3d(1.03, 1.03, 1.03); - } - - 80% { - transform: scale3d(.97, .97, .97); - } - - to { - opacity: 1; - transform: scale3d(1, 1, 1); - } -} - -.bounceIn { - animation-name: bounceIn; -} \ No newline at end of file diff --git a/css/style.css b/css/style.css index 53aa45bb0555f9b05f478972580db200e04f0621..91f0f6062221109fd82f1427ebd4a6586066047a 100644 --- a/css/style.css +++ b/css/style.css @@ -108,6 +108,10 @@ body { font-size: 42px; } +.page-title-wrapper a { + color: #fff; +} + #main-footer-wrapper { position: fixed; bottom: 0; diff --git a/include/admin.php b/include/admin.php index 3e3b7fafade74e09d1f090f98b17811460ff1711..8aab087497561b437f627cb43f5452008286c53e 100644 --- a/include/admin.php +++ b/include/admin.php @@ -25,9 +25,57 @@ function checkUser() { Flight::route('GET /admin', function() { checkUser(); - global $VERSION; + global $locator; Flight::render('admin/index.php', array('title' => 'Admin panel', - 'version' => $VERSION)); + 'version' => $locator->getVersion(), + 'contextRoot' => $locator->config->contextRoot)); +}); + +Flight::route('GET /admin-join', function() { + checkUser(); + + global $locator; + Flight::render('admin/join.php', array('title' => 'Admin panel - Join users', + 'version' => $locator->getVersion(), + 'contextRoot' => $locator->config->contextRoot)); +}); + +Flight::route('POST /admin-join', function() { + checkUser(); + + global $locator; + + $user1Id = filter_input(INPUT_POST, 'user1', FILTER_SANITIZE_STRING); + $user2Id = filter_input(INPUT_POST, 'user2', FILTER_SANITIZE_STRING); + + if ($user1Id === null) { + throw new \RAP\BadRequestException("Missing parameter user1"); + } + if ($user2Id === null) { + throw new \RAP\BadRequestException("Missing parameter user2"); + } + + $dao = $locator->getUserDAO(); + $user1 = $dao->findUserById($user1Id); + $user2 = $dao->findUserById($user2Id); + + $locator->getUserHandler()->joinUsers($user1, $user2); + + Flight::redirect($locator->getBasePath() . '/admin-join'); +}); + +Flight::route('GET /admin-search', function() { + checkUser(); + + $searchText = Flight::request()->query['query']; + if ($searchText === null) { + throw new \RAP\BadRequestException("Missing query parameter"); + } + + global $locator; + $users = $locator->getUserDAO()->searchUser($searchText); + + Flight::json($users); }); Flight::route('GET /admin/oauth2_clients', function() { diff --git a/include/footer.php b/include/footer.php index 39fdc446951d8e3936a02716fb077050de1e152a..d61aed3134f805ca3b37be42724bc940ad78c5b2 100644 --- a/include/footer.php +++ b/include/footer.php @@ -2,10 +2,8 @@ <div class="row"> <div class="col-sm-8 col-sm-offset-2"> <div class="alert alert-info text-center"> - <div class="animated bounceIn hinge"> - <span class="glyphicon glyphicon-info-sign"></span> - <strong>Need help?</strong> Please read our <a href="https://sso.ia2.inaf.it/home/index.php?lang=en"><u>User guide</u></a> and <a href="https://sso.ia2.inaf.it/home/faq.php?lang=en"><u>FAQ</u></a>. - </div> + <span class="glyphicon glyphicon-info-sign"></span> + <strong>Need help?</strong> Please read our <a href="https://sso.ia2.inaf.it/home/index.php?lang=en" target="blank_"><u>User guide</u></a> and <a href="https://sso.ia2.inaf.it/home/faq.php?lang=en" target="blank_"><u>FAQ</u></a>. </div> </div> </div> @@ -23,7 +21,7 @@ <p id="footer-credits">This software has been adapted by the IA2 team from the Remote Authentication Portal written by Franco Tinarelli at INAF-IRA.</p> <div id="main-footer"> Powered by - <img src="img/logo-ia2-small.png" alt="logo IA2" /> + <img src="<?php echo $contextRoot; ?>/img/logo-ia2-small.png" alt="logo IA2" /> <a href="http://www.ia2.inaf.it/" target="blank_">IA2</a> </div> </footer> diff --git a/include/front-controller.php b/include/front-controller.php index 135852847a09f260b3b54d0537edcbf04f222eb5..07fec11511c56268ffe05b20481ad603704ab68f 100644 --- a/include/front-controller.php +++ b/include/front-controller.php @@ -54,6 +54,7 @@ Flight::route('/', function() { $clients = $locator->getOAuth2ClientDAO()->getOAuth2Clients(); Flight::render('services-list.php', array('title' => 'RAP', 'version' => $locator->getVersion(), + 'contextRoot' => $locator->config->contextRoot, 'clients' => $clients, 'action' => $locator->getBasePath() . '/')); break; @@ -62,8 +63,11 @@ Flight::route('/', function() { function renderMainPage(RAP\AuthPageModel $authPageModel) { global $locator; - Flight::render('main-page.php', array('title' => 'RAP', - 'version' => $locator->getVersion(), 'model' => $authPageModel)); + Flight::render('main-page.php', array( + 'title' => 'RAP', + 'version' => $locator->getVersion(), + 'contextRoot' => $locator->config->contextRoot, + 'model' => $authPageModel)); } Flight::route('GET /auth/oauth2/authorize', function() { @@ -234,17 +238,18 @@ Flight::route('/local', function() { Flight::route('GET /x509-name-surname', function() { session_start(); - global $locator, $BASE_PATH, $VERSION; + global $locator; $session = $locator->getSession(); if ($session->getX509DataToRegister() !== null && $session->getX509DataToRegister()->name === null) { Flight::render('x509-name-surname.php', array('title' => 'Select name and surname', - 'version' => $VERSION, + 'version' => $locator->getVersion(), + 'contextRoot' => $locator->config->contextRoot, 'fullName' => $session->getX509DataToRegister()->fullName, 'candidateNames' => $session->getX509DataToRegister()->candidateNames)); } else { // Redirect to index - header("Location: " . $BASE_PATH); + header("Location: " . $locator->getBasePath()); die(); } }); @@ -286,6 +291,7 @@ Flight::route('GET /tou-check', function() { Flight::render('tou-check.php', array('title' => 'Terms of Use acceptance', 'user' => $locator->getSession()->getUser(), 'version' => $locator->getVersion(), + 'contextRoot' => $locator->config->contextRoot, 'registration_url' => $locator->getBasePath() . '/register')); } }); @@ -301,7 +307,8 @@ Flight::route('GET /confirm-join', function() { Flight::render('confirm-join.php', array('title' => 'Confirm join', 'user' => $locator->getSession()->getUser(), 'user_to_join' => $locator->getSession()->getUserToJoin(), - 'version' => $locator->getVersion())); + 'version' => $locator->getVersion(), + 'contextRoot' => $locator->config->contextRoot)); } }); @@ -353,9 +360,10 @@ Flight::route('GET /account', function () { } else { $admin = $locator->getUserDAO()->isAdmin($user->id); Flight::render('account-management.php', array('title' => 'RAP Account Management', - 'version' => $locator->getVersion(), 'session' => $locator->getSession(), - 'admin' => $admin, - 'contextRoot' => $locator->config->contextRoot)); + 'version' => $locator->getVersion(), + 'contextRoot' => $locator->config->contextRoot, + 'session' => $locator->getSession(), + 'admin' => $admin)); } }); @@ -376,11 +384,12 @@ Flight::route('GET /token-issuer', function () { if ($user === null) { Flight::redirect('/'); } else { - $admin = $locator->getUserDAO()->isAdmin($user->id); Flight::render('token-issuer.php', array('title' => 'RAP Token Issuer', - 'version' => $locator->getVersion(), 'session' => $locator->getSession(), - 'config' => $config, 'csrfToken' => $csrfToken, - 'contextRoot' => $locator->config->contextRoot)); + 'version' => $locator->getVersion(), + 'contextRoot' => $locator->config->contextRoot, + 'session' => $locator->getSession(), + 'config' => $config, + 'csrfToken' => $csrfToken)); } }); diff --git a/include/header.php b/include/header.php index 2935bed635faed050cec2fdce1e1245c32f13970..d65320243d12a38f60d249241dc074602a570897 100644 --- a/include/header.php +++ b/include/header.php @@ -7,9 +7,8 @@ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous" /> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> - <link rel="stylesheet" href="css/style.css?v=2" /> - <link rel="stylesheet" href="css/animation.css?v=2" /> - <script src="js/script.js?v=<?php echo $version; ?>"></script> + <link rel="stylesheet" href="<?php echo $contextRoot; ?>/css/style.css?v=<?php echo $version; ?>" /> + <script src="<?php echo $contextRoot; ?>/js/script.js?v=<?php echo $version; ?>"></script> </head> <body> <header id="main-header"> @@ -17,7 +16,8 @@ Image Credits & Copyright: Colombari/E.Recurt </div> <div class="page-title-wrapper"> - <h1 class="text-center">Remote Authentication Portal + <h1 class="text-center"> + <a href="<?php echo $contextRoot; ?>/">Remote Authentication Portal</a> <span class="circle-wrapper"> <div class="circle">beta<br/>version!</div> </span> diff --git a/js/admin-join.js b/js/admin-join.js new file mode 100644 index 0000000000000000000000000000000000000000..bd5f820a17707139988882a07919f798aa050f23 --- /dev/null +++ b/js/admin-join.js @@ -0,0 +1,118 @@ +(function () { + + function getUserPanel(user) { + $panelBody = $('<div class="panel-body"></div>'); + for (var i = 0; i < user.identities.length; i++) { + identity = user.identities[i]; + $identityEl = $('<dl class="dl-horizontal"><dt>'); + $identityEl.append('<dt>Type</dt>'); + $identityEl.append('<dd>' + identity.type + '</dd>'); + $identityEl.append('<dt>Email</dt>'); + $identityEl.append('<dd>' + identity.email + '</dd>'); + if (identity.name) { + $identityEl.append('<dt>Name</dt>'); + $identityEl.append('<dd>' + identity.name + '</dd>'); + } + if (identity.surname) { + $identityEl.append('<dt>Surname</dt>'); + $identityEl.append('<dd>' + identity.surname + '</dd>'); + } + $panelBody.append($identityEl); + } + $panel = $('<div class="panel panel-default"></div>'); + $panel.append($panelBody); + return $panel; + } + + function getUser(usersList, id) { + for (var i = 0; i < usersList.length; i++) { + var user = usersList[i]; + if (user.id === id) { + return user; + } + } + return null; + } + + function createSelectUserFunction(usersList, $container) { + return function (event) { + $container.empty(); + var user = getUser(usersList, $(event.target).val()); + if (user) { + $container.append(getUserPanel(user)); + } + }; + } + + function createSearchFunctionTimeout(input, select, usersList) { + var text = $(input).val(); + return function () { + $.ajax({ + url: 'admin-search?query=' + text, + type: "GET", + dataType: 'json', + xhrFields: { + withCredentials: true + }, + success: function (users) { + // empty usersList: + usersList.splice(0, usersList.length); + var selectEl = $(select); + selectEl.empty(); + for (var i = 0; i < users.length; i++) { + var user = users[i]; + usersList.push(user); + var label = null; + var types = []; + var primaryIdentity = null; + for (var j = 0; j < user.identities.length; j++) { + var identity = user.identities[j]; + if (identity.name !== null && identity.surname !== null) { + label = identity.name + " " + identity.surname; + } + if (identity.primary) { + primaryIdentity = identity; + } + types.push(identity.type); + } + if (label === null) { + label = primaryIdentity.email; + } + label += " (" + types.join(", ") + ") [" + user.id + "]"; + selectEl.append('<option value="' + user.id + '">' + label + "</option>"); + } + selectEl.attr("disabled", users.length === 0); + $('#join-btn-admin').attr("disabled", users1.length === 0 || users2.length === 0); + selectEl.trigger('change'); + } + }); + }; + } + + function createSearchFunction(timer, input, select, usersList) { + return function () { + clearTimeout(timer); + timer = setTimeout(createSearchFunctionTimeout(input, select, usersList), 500); + }; + } + + var timer1, timer2; + + var input1 = $('#user1_search'); + var input2 = $('#user2_search'); + var select1 = $('#user1_select'); + var select2 = $('#user2_select'); + + var users1 = [], users2 = []; + + var search1 = createSearchFunction(timer1, input1, select1, users1); + var search2 = createSearchFunction(timer2, input2, select2, users2); + + input1.on('keydown', search1); + input1.on('input', search1); + input2.on('keydown', search2); + input2.on('input', search2); + + select1.on('change', createSelectUserFunction(users1, $('#user1_content'))); + select2.on('change', createSelectUserFunction(users2, $('#user2_content'))); +})(); \ No newline at end of file diff --git a/version.txt b/version.txt index 359a5b952d49f3592571e2af081510656029298e..10bf840ed530af123660f5edb1544264d8f2def4 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.0.0 \ No newline at end of file +2.0.1 \ No newline at end of file diff --git a/views/account-management.php b/views/account-management.php index 39712aba8c5797b11af5818f15d1232a6d90a8d6..21e4cdf15147c3ff97b4e44437c5ca4bcc1141cf 100644 --- a/views/account-management.php +++ b/views/account-management.php @@ -35,9 +35,10 @@ include 'include/header.php'; <?php if ($admin) { ?> <div class="row"> <div class="col-sm-12"> - <a class="btn btn-default" href="<?php echo $contextRoot; ?>/admin" title="Admin panel" data-toggle="tooltip" data-placement="bottom"> + <h4>Admin tools</h4> + <a class="btn btn-default" href="<?php echo $contextRoot; ?>/admin-join" title="Join users" data-toggle="tooltip" data-placement="bottom"> <span class="glyphicon glyphicon-wrench"></span> - Admin + Join Users </a> </div> </div> diff --git a/views/admin/join.php b/views/admin/join.php new file mode 100644 index 0000000000000000000000000000000000000000..03de8116f5601b2cdc3b29483cb8f9ba804091a6 --- /dev/null +++ b/views/admin/join.php @@ -0,0 +1,37 @@ +<?php +include 'include/header.php'; +?> + +<h1 class="text-center"><a href="<?php echo $contextRoot; ?>/account">Account management</a> - Join users</h1> + +<form method="POST" action="<?php echo $contextRoot . '/admin-join'; ?>"> + <div class="row"> + <div class="col-xs-6"> + <h2>User 1</h2> + <input type="text" class="form-control" id="user1_search" placeholder="Search..." /><br/> + <select class="form-control" disabled="disabled" name="user1" id="user1_select"></select> + <br/> + <div id="user1_content"></div> + </div> + <div class="col-xs-6"> + <h2>User 2</h2> + <input type="text" class="form-control" id="user2_search" placeholder="Search..." /><br/> + <select class="form-control" disabled="disabled" name="user2" id="user2_select"></select> + <br/> + <div id="user2_content"></div> + </div> + </div> + + <div class="row"> + <div class="col-xs-12 text-center"> + <input type="submit" class="btn btn-primary btn-lg" disabled="disabled" id="join-btn-admin" value="Join users" /> + </div> + </div> +</form> + +<script src="js/admin-join.js"></script> + + +<br/><br/><br/> +<?php +include 'include/footer.php';