logout and homepage

This commit is contained in:
Michele Artini 2020-11-04 15:06:34 +01:00
parent 6a5488811e
commit 94637fe6dd
6 changed files with 328 additions and 355 deletions

View File

@ -1,44 +0,0 @@
package eu.dnetlib.organizations;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import eu.dnetlib.organizations.controller.UserInfo;
import eu.dnetlib.organizations.utils.AuthenticationUtils;
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
private static Logger logger = LoggerFactory.getLogger(MyAccessDeniedHandler.class);
@Override
public void handle(final HttpServletRequest req, final HttpServletResponse res, final AccessDeniedException e)
throws IOException, ServletException {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
logger.warn(String
.format("User '%s' (%s) attempted to access the protected URL: %s", AuthenticationUtils.extractEmail(authentication), req.getRemoteAddr(), req
.getRequestURI()));
}
if (UserInfo.isNotAuthorized(authentication)) {
res.sendRedirect(req.getContextPath() + "/authorizationRequest");
} else {
res.sendRedirect(req.getContextPath() + "/alreadyRegistered");
}
}
}

View File

@ -4,24 +4,32 @@ import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.web.access.AccessDeniedHandler;
import eu.dnetlib.organizations.controller.UserInfo;
import eu.dnetlib.organizations.controller.UserRole;
import eu.dnetlib.organizations.model.User;
import eu.dnetlib.organizations.repository.UserRepository;
import eu.dnetlib.organizations.utils.AuthenticationUtils;
@Configuration
@EnableWebSecurity
@ -31,13 +39,15 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private UserRepository userRepository;
@Autowired
private AccessDeniedHandler accessDeniedHandler;
private ClientRegistrationRepository clientRegistrationRepository;
@Value("${openaire.api.valid.subnet}")
private String openaireApiValidSubnet;
private static String OPENORGS_ROLE_PREFIX = "OPENORGS_";
private static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);
private static final String[] VALID_ROLES = {
OPENORGS_ROLE_PREFIX + UserRole.ADMIN,
OPENORGS_ROLE_PREFIX + UserRole.NATIONAL_ADMIN,
@ -48,15 +58,14 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/", "/api/**")
.antMatchers("/main", "/api/**")
.hasAnyRole(VALID_ROLES)
.antMatchers("/registration_api/**")
.hasRole(NOT_AUTORIZED_ROLE)
.antMatchers("/resources/**", "/webjars/**")
.antMatchers("/", "/resources/**", "/webjars/**")
.permitAll()
.antMatchers("/oa_api/**")
.hasIpAddress(openaireApiValidSubnet)
@ -64,10 +73,33 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
.authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.accessDeniedHandler(accessDeniedHandler())
.and()
.logout()
.logoutSuccessHandler(oidcLogoutSuccessHandler())
.and()
.oauth2Login(oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.oidcUserService(this.oidcUserService())));
}
private AccessDeniedHandler accessDeniedHandler() {
return (req, res, e) -> {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
logger.warn(String
.format("User '%s' (%s) attempted to access the protected URL: %s", AuthenticationUtils.extractEmail(authentication), req
.getRemoteAddr(), req.getRequestURI()));
}
if (UserInfo.isNotAuthorized(authentication)) {
res.sendRedirect(req.getContextPath() + "/authorizationRequest");
} else {
res.sendRedirect(req.getContextPath() + "/alreadyRegistered");
}
};
}
private OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler() {
return new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository);
}
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {

View File

@ -11,6 +11,11 @@ public class HomeController {
return "home";
}
@GetMapping("/main")
public String main() {
return "main";
}
@GetMapping("/login")
public String login() {
return "login";

View File

@ -1,229 +1,78 @@
<!doctype html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="Cache-Control"
content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="resources/css/bootstrap.min.css" />
<!-- Icons CSS -->
<link rel="stylesheet" href="resources/css/fontawesome-all.min.css">
<style type="text/css">
.table > tbody > tr > td { vertical-align : middle !important; }
.card > .table { margin-bottom : 0 !important; }
fieldset > legend { font-size : 1.2rem !important; }
.overlaydiv {
position: fixed;
width: 100%;
height: 100%;
z-index: 10000;
visibility: hidden;
}
.grayRectangle {
position: absolute;
background-color: black;
opacity:0.6;
top: 30%;
left: 40%;
width: 20%;
height: 20%;
z-index: 100;
border-radius: 15px;
}
</style>
<title>Organizations Database</title>
<title>Organizations Database: Login</title>
</head>
<body ng-app="orgs" sec:authorize="isAuthenticated()">
<body>
<div id="spinnerdiv" class="overlaydiv">
<span class="grayRectangle"><!--The spinner is added on loading here--></span>
<div>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<a class="navbar-brand" href="#">
<img src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenOrgs Database"> OpenOrgs Database
</a>
</nav>
</div>
<div class="container-fluid">
<div class="row" style="margin-top: 20px">
<div class="col-xs-12 col-sm-10 col-md-8 col-lg-4 offset-sm-1 offset-md-2 offset-lg-4">
<div class="card">
<div class="card-header">
<ul id="tabList" class="nav nav-tabs card-header-tabs">
<li class="nav-item"><a href="#tabLogin" class="nav-link active">Sign In</a></li>
<li class="nav-item"><a href="#tabRegister" class="nav-link">Info</a></li>
</ul>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane active text-center p-5" id="tabLogin">
<a href="./main" class="btn btn-lg btn-primary">Access using OpenAIRE credentials</a>
</div>
<div class="tab-pane" id="tabRegister">
<h4 class="card-title">Access Guide</h4>
<div id="registerMessage">
<p class="card-text mb-4">
To use this service you have to perform the following steps:
<ol>
<li>Register on the OpenAIRE portal.</li>
<li>Login using the OpenAIRE credentials</li>
<li>Compile the Authorization Request Form</li>
<li>An administrator will authorize you as soon as possible</li>
</ol>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary" ng-controller="menuCtrl">
<a class="navbar-brand" href="#"> <img
src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenOrgs Database">
OpenOrgs Database
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse"
data-target="#navbarSupportedContent">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse w-100 order-1" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Search</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#!/search">simple search</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#!/countries/1" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">browse by country</a>
<a class="dropdown-item" href="#!/countries/0" sec:authorize="hasRole('ROLE_OPENORGS_USER')">browse by country</a>
<a class="dropdown-item" href="#!/types/1" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">browse by type</a>
<a class="dropdown-item" href="#!/types/0" sec:authorize="hasRole('ROLE_OPENORGS_USER')">browse by type</a>
</div>
</li>
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Curation</a>
<div class="dropdown-menu">
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/pendings/_"><span class="pr-2">suggested</span><span class="badge badge-primary badge-pill">{{info.data.total.nPendingOrgs}}</span></a>
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/duplicates/_"><span class="pr-2">new duplicates</span><span class="badge badge-primary badge-pill">{{info.data.total.nDuplicates}}</span></a>
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/conflicts/_"><span class="pr-2">potential conflicts</span><span class="badge badge-danger badge-pill">{{info.data.total.nConflicts}}</span></a>
</div>
</li>
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">New</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#!/new">new organization</a>
</div>
</li>
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_OPENORGS_USER')">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Curation</a>
<div class="dropdown-menu">
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/duplicates/_"><span class="pr-2">new duplicates</span><span class="badge badge-primary badge-pill">{{info.data.total.nDuplicates}}</span></a>
</div>
</li>
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_OPENORGS_USER')">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Suggest</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#!/new">suggest a new organization</a>
</div>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown"><i class="fa fa-cog"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="#!/users">manage users</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown"><i class="fa fa-user"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<p class="px-4 pt-2 text-muted small">
<b>Logged as:</b><br /><span sec:authentication="principal.email" id="current_user"></span><br />
<b>Role:</b><br /><span sec:authentication="principal.authorities"></span>
</p>
<div class="dropdown-divider"></div>
<a class="dropdown-item" th:href="@{/logout}">Logout</a>
</div>
</li>
<li class="nav-item"><a class="btn btn-outline-secondary" href="doc" target="_blank">API</a></li>
</ul>
</div>
</nav>
<div class="container-fluid small mt-4" ng-view></div>
<script sec:authorize="hasRole('ROLE_OPENORGS_ADMIN')">
function superAdminMode() { return true; }
</script>
<script sec:authorize="!hasRole('ROLE_OPENORGS_ADMIN')">
function superAdminMode() { return false; }
</script>
<script sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">
function adminMode() { return true; }
</script>
<script sec:authorize="!hasRole('ROLE_OPENORGS_ADMIN') and !hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">
function adminMode() { return false; }
</script>
<script>
function currentUser() { return document.getElementById('current_user').textContent; }
</script>
<script src="resources/js/jquery-3.4.1.min.js"></script>
<script src="resources/js/popper.min.js"></script>
<script src="resources/js/bootstrap.min.js"></script>
<script src="resources/js/angular.min.js"></script>
<script src="resources/js/angular-route.min.js"></script>
<script src='resources/js/checklist-model.js'></script>
<script src="resources/js/organizations.js"></script>
<script src="resources/js/spin.js"></script>
<script>
// Spinner show/hide methods ~ Andrea Mannocci
var spinnerOpts = {
lines: 15,
length: 16,
width: 5,
radius: 25,
color: '#eeeeee',
className: 'spinner',
top: '40%'
};
var spinnerTarget = document.getElementById('spinnerdiv');
var spinner;
function showSpinner() {
spinner = new Spinner(spinnerOpts).spin(spinnerTarget);
spinnerTarget.style.visibility = 'visible';
}
function hideSpinner() {
spinnerTarget.style.visibility = 'hidden';
spinner.stop();
}
function call_http_get($http, url, onSuccess) {
showSpinner();
$http.get(url).then(function successCallback(res) {
hideSpinner();
if((typeof res.data) == 'string') {
alert("Session expired !"); location.reload(true);
} else {
onSuccess(res);
}
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
function call_http_post($http, url, obj, onSuccess) {
showSpinner();
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
$http.post(url, obj).then(function successCallback(res) {
hideSpinner();
if ((typeof res.data) == 'string') {
alert("Session expired !"); location.reload(true);
} else {
onSuccess(res);
}
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
function call_http_delete($http, url, onSuccess) {
showSpinner();
$http.delete(url).then(function successCallback(res) {
hideSpinner();
if ((typeof res.data) == 'string') {
alert("Session expired !"); location.reload(true);
} else {
onSuccess(res);
}
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
</script>
$('#tabList a').on('click', function (e) {
e.preventDefault()
$(this).tab('show')
});
</script>
</body>
</html>

View File

@ -1,98 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="Cache-Control"
content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="resources/css/bootstrap.min.css" />
<title>Organizations Database: Login</title>
</head>
<body>
<div>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<a class="navbar-brand" href="#">
<img src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenOrgs Database"> OpenOrgs Database
</a>
</nav>
</div>
<div class="container-fluid">
<div class="row" style="margin-top: 20px">
<div class="col-xs-12 col-sm-10 col-md-8 col-lg-4 offset-sm-1 offset-md-2 offset-lg-4">
<div class="card">
<div class="card-header">
<ul id="tabList" class="nav nav-tabs card-header-tabs">
<li class="nav-item"><a href="#tabLogin" class="nav-link active">Sign In</a></li>
<li class="nav-item"><a href="#tabRegister" class="nav-link">Register</a></li>
</ul>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane active" id="tabLogin">
<h4 class="card-title">Please Sign In</h4>
<form th:action="@{/login}" method="post">
<div th:if="${param.error}">
<div class="alert alert-danger">Invalid username and password.</div>
</div>
<div th:if="${param.logout}">
<div class="alert alert-info">You have been logged out.</div>
</div>
<div class="form-group">
<input type="text" name="username" id="username" class="form-control input-lg" placeholder="Email" required="true" autofocus="true" />
</div>
<div class="form-group">
<input type="password" name="password" id="password" class="form-control input-lg" placeholder="Password" required="true" />
</div>
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6">
<input type="submit" class="btn btn-lg btn-primary btn-block" value="Sign In" />
</div>
<div class="col-xs-6 col-sm-6 col-md-6"></div>
</div>
</form>
</div>
<div class="tab-pane" id="tabRegister">
<h4 class="card-title">Register</h4>
<div id="registerMessage">
<p class="card-text mb-4">
To use this service you have to perform the following steps:
<ol>
<li>Register on the OpenAIRE portal.</li>
<li>Login using the OpenAIRE credentials</li>
<li>Compile the Authorization Request Form</li>
<li>An administrator will authorize you as soon as possible</li>
</ol>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="resources/js/jquery-3.4.1.min.js"></script>
<script src="resources/js/popper.min.js"></script>
<script src="resources/js/bootstrap.min.js"></script>
<script>
$('#tabList a').on('click', function (e) {
e.preventDefault()
$(this).tab('show')
});
</script>
</body>
</html>

View File

@ -0,0 +1,229 @@
<!doctype html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="resources/css/bootstrap.min.css" />
<!-- Icons CSS -->
<link rel="stylesheet" href="resources/css/fontawesome-all.min.css">
<style type="text/css">
.table > tbody > tr > td { vertical-align : middle !important; }
.card > .table { margin-bottom : 0 !important; }
fieldset > legend { font-size : 1.2rem !important; }
.overlaydiv {
position: fixed;
width: 100%;
height: 100%;
z-index: 10000;
visibility: hidden;
}
.grayRectangle {
position: absolute;
background-color: black;
opacity:0.6;
top: 30%;
left: 40%;
width: 20%;
height: 20%;
z-index: 100;
border-radius: 15px;
}
</style>
<title>Organizations Database</title>
</head>
<body ng-app="orgs" sec:authorize="isAuthenticated()">
<div id="spinnerdiv" class="overlaydiv">
<span class="grayRectangle"><!--The spinner is added on loading here--></span>
</div>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary" ng-controller="menuCtrl">
<a class="navbar-brand" href="#"> <img
src="resources/images/openaire_logo_small.png" width="30" height="30" alt="OpenOrgs Database">
OpenOrgs Database
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse"
data-target="#navbarSupportedContent">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse w-100 order-1" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Search</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#!/search">simple search</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#!/countries/1" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">browse by country</a>
<a class="dropdown-item" href="#!/countries/0" sec:authorize="hasRole('ROLE_OPENORGS_USER')">browse by country</a>
<a class="dropdown-item" href="#!/types/1" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">browse by type</a>
<a class="dropdown-item" href="#!/types/0" sec:authorize="hasRole('ROLE_OPENORGS_USER')">browse by type</a>
</div>
</li>
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Curation</a>
<div class="dropdown-menu">
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/pendings/_"><span class="pr-2">suggested</span><span class="badge badge-primary badge-pill">{{info.data.total.nPendingOrgs}}</span></a>
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/duplicates/_"><span class="pr-2">new duplicates</span><span class="badge badge-primary badge-pill">{{info.data.total.nDuplicates}}</span></a>
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/conflicts/_"><span class="pr-2">potential conflicts</span><span class="badge badge-danger badge-pill">{{info.data.total.nConflicts}}</span></a>
</div>
</li>
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">New</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#!/new">new organization</a>
</div>
</li>
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_OPENORGS_USER')">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Curation</a>
<div class="dropdown-menu">
<a class="dropdown-item d-flex justify-content-between align-items-center" href="#!/duplicates/_"><span class="pr-2">new duplicates</span><span class="badge badge-primary badge-pill">{{info.data.total.nDuplicates}}</span></a>
</div>
</li>
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_OPENORGS_USER')">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Suggest</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#!/new">suggest a new organization</a>
</div>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown"><i class="fa fa-cog"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="#!/users">manage users</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown"><i class="fa fa-user"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<p class="px-4 pt-2 text-muted small">
<b>Logged as:</b><br /><span sec:authentication="principal.email" id="current_user"></span><br />
<b>Role:</b><br /><span sec:authentication="principal.authorities"></span>
</p>
<div class="dropdown-divider"></div>
<a class="dropdown-item" th:href="@{/logout}">Logout</a>
</div>
</li>
<li class="nav-item"><a class="btn btn-outline-secondary" href="doc" target="_blank">API</a></li>
</ul>
</div>
</nav>
<div class="container-fluid small mt-4" ng-view></div>
<script sec:authorize="hasRole('ROLE_OPENORGS_ADMIN')">
function superAdminMode() { return true; }
</script>
<script sec:authorize="!hasRole('ROLE_OPENORGS_ADMIN')">
function superAdminMode() { return false; }
</script>
<script sec:authorize="hasRole('ROLE_OPENORGS_ADMIN') or hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">
function adminMode() { return true; }
</script>
<script sec:authorize="!hasRole('ROLE_OPENORGS_ADMIN') and !hasRole('ROLE_OPENORGS_NATIONAL_ADMIN')">
function adminMode() { return false; }
</script>
<script>
function currentUser() { return document.getElementById('current_user').textContent; }
</script>
<script src="resources/js/jquery-3.4.1.min.js"></script>
<script src="resources/js/popper.min.js"></script>
<script src="resources/js/bootstrap.min.js"></script>
<script src="resources/js/angular.min.js"></script>
<script src="resources/js/angular-route.min.js"></script>
<script src='resources/js/checklist-model.js'></script>
<script src="resources/js/organizations.js"></script>
<script src="resources/js/spin.js"></script>
<script>
// Spinner show/hide methods ~ Andrea Mannocci
var spinnerOpts = {
lines: 15,
length: 16,
width: 5,
radius: 25,
color: '#eeeeee',
className: 'spinner',
top: '40%'
};
var spinnerTarget = document.getElementById('spinnerdiv');
var spinner;
function showSpinner() {
spinner = new Spinner(spinnerOpts).spin(spinnerTarget);
spinnerTarget.style.visibility = 'visible';
}
function hideSpinner() {
spinnerTarget.style.visibility = 'hidden';
spinner.stop();
}
function call_http_get($http, url, onSuccess) {
showSpinner();
$http.get(url).then(function successCallback(res) {
hideSpinner();
if((typeof res.data) == 'string') {
alert("Session expired !"); location.reload(true);
} else {
onSuccess(res);
}
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
function call_http_post($http, url, obj, onSuccess) {
showSpinner();
$http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
$http.post(url, obj).then(function successCallback(res) {
hideSpinner();
if ((typeof res.data) == 'string') {
alert("Session expired !"); location.reload(true);
} else {
onSuccess(res);
}
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
function call_http_delete($http, url, onSuccess) {
showSpinner();
$http.delete(url).then(function successCallback(res) {
hideSpinner();
if ((typeof res.data) == 'string') {
alert("Session expired !"); location.reload(true);
} else {
onSuccess(res);
}
}, function errorCallback(res) {
hideSpinner();
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
}
</script>
</body>
</html>