institutional logo implementation

This commit is contained in:
anhefti 2019-02-14 16:54:48 +01:00
parent 6b8ef6c694
commit 64f10c6455
18 changed files with 412 additions and 195 deletions

View file

@ -57,6 +57,8 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@Order(6)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ErrorController {
@Value("${sebserver.webservice.api.admin.endpoint}")
private String adminEndpoint;
@Value("${sebserver.webservice.api.redirect.unauthorized}")
private String unauthorizedRedirect;
@ -82,7 +84,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
web
.ignoring()
.antMatchers("/error")
.antMatchers(API.LOGO_ENDPOINT + "/*");
.antMatchers(this.adminEndpoint + API.INFO_ENDPOINT + "/**");
}
@RequestMapping("/error")

View file

@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.gbl.api;
public final class API {
public static final String PARAM_LOGO_IMAGE = "logoImageBase64";
public static final String PARAM_INSTITUTION_ID = "institutionId";
public static final String PARAM_MODEL_ID = "modelId";
public static final String PARAM_ENTITY_TYPE = "entityType";
@ -17,8 +18,13 @@ public final class API {
public static final String INSTITUTION_VAR_PATH_SEGMENT = "/{" + PARAM_INSTITUTION_ID + "}";
public static final String MODEL_ID_VAR_PATH_SEGMENT = "/{" + PARAM_MODEL_ID + "}";
public static final String LOGO_ENDPOINT = "/logo";
public static final String INSTITUTIONAL_LOGO_PATH = LOGO_ENDPOINT + INSTITUTION_VAR_PATH_SEGMENT;
public static final String INFO_ENDPOINT = "/info";
public static final String LOGO_PATH_SEGMENT = "/logo";
public static final String INSTITUTIONAL_LOGO_PATH = LOGO_PATH_SEGMENT + "/{urlSuffix}";
public static final String PRIVILEGES_PATH_SEGMENT = "/privileges";
public static final String PRIVILEGES_ENDPOINT = INFO_ENDPOINT + PRIVILEGES_PATH_SEGMENT;
public static final String INSTITUTION_ENDPOINT = "/institution";

View file

@ -19,28 +19,28 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
public final class Privilege {
/** The RoleTypeKey defining the UserRole and EntityType for this Privilege */
@JsonProperty
@JsonProperty("roleTypeKey")
public final RoleTypeKey roleTypeKey;
/** Defines a base-privilege type that defines the overall access for an entity-type */
@JsonProperty
@JsonProperty("basePrivilege")
public final PrivilegeType basePrivilege;
/** Defines an institutional privilege type that defines the institutional restricted access for a
* entity-type */
@JsonProperty
@JsonProperty("institutionalPrivilege")
public final PrivilegeType institutionalPrivilege;
/** Defines an ownership privilege type that defines the ownership restricted access for a entity-type */
@JsonProperty
@JsonProperty("ownershipPrivilege")
public final PrivilegeType ownershipPrivilege;
@JsonCreator
public Privilege(
final RoleTypeKey roleTypeKey,
final PrivilegeType basePrivilege,
final PrivilegeType institutionalPrivilege,
final PrivilegeType ownershipPrivilege) {
@JsonProperty("roleTypeKey") final RoleTypeKey roleTypeKey,
@JsonProperty("basePrivilege") final PrivilegeType basePrivilege,
@JsonProperty("institutionalPrivilege") final PrivilegeType institutionalPrivilege,
@JsonProperty("ownershipPrivilege") final PrivilegeType ownershipPrivilege) {
this.roleTypeKey = roleTypeKey;
this.basePrivilege = basePrivilege;
@ -85,13 +85,16 @@ public final class Privilege {
/** A key that combines UserRole EntityType identity */
public static final class RoleTypeKey {
@JsonProperty
@JsonProperty("entityType")
public final EntityType entityType;
@JsonProperty
@JsonProperty("userRole")
public final UserRole userRole;
@JsonCreator
public RoleTypeKey(final EntityType type, final UserRole role) {
public RoleTypeKey(
@JsonProperty("entityType") final EntityType type,
@JsonProperty("userRole") final UserRole role) {
this.entityType = type;
this.userRole = role;
}

View file

@ -8,22 +8,13 @@
package ch.ethz.seb.sebserver.gui;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
@ -35,10 +26,8 @@ import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
@Order(4)
public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${sebserver.gui.entrypoint}")
private String guiEndpointPath;
@Value("${sebserver.webservice.api.redirect.unauthorized}")
private String unauthorizedRedirect;
@Autowired
private InstitutionalAuthenticationEntryPoint institutionalAuthenticationEntryPoint;
/** Gui-service related public URLS from spring web security perspective */
public static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
@ -67,19 +56,7 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter {
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(
new AuthenticationEntryPoint() {
@Override
public void commence(
final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException) throws IOException, ServletException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.sendRedirect(GuiWebsecurityConfig.this.unauthorizedRedirect);
}
})
.authenticationEntryPoint(this.institutionalAuthenticationEntryPoint)
.and()
.formLogin().disable()
.httpBasic().disable()

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService;
@Lazy
@Component
final class InstitutionalAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger log = LoggerFactory.getLogger(InstitutionalAuthenticationEntryPoint.class);
private final String guiEntryPoint;
private final String guiRedirect;
private final WebserviceURIService webserviceURIService;
private final ClientHttpRequestFactory clientHttpRequestFactory;
protected InstitutionalAuthenticationEntryPoint(
@Value("${sebserver.gui.entrypoint}") final String guiEntryPoint,
@Value("${sebserver.webservice.api.redirect.unauthorized}") final String guiRedirect,
final WebserviceURIService webserviceURIService,
final ClientHttpRequestFactory clientHttpRequestFactory) {
this.guiEntryPoint = guiEntryPoint;
this.guiRedirect = guiRedirect;
this.webserviceURIService = webserviceURIService;
this.clientHttpRequestFactory = clientHttpRequestFactory;
}
@Override
public void commence(
final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException) throws IOException, ServletException {
final String requestURI = request.getRequestURI();
log.info("No default gui entrypoint requested: {}", requestURI);
final String logoImageBase64 = requestLogoImage(requestURI);
if (StringUtils.isNoneBlank(logoImageBase64)) {
// forward
request.getSession().setAttribute(API.PARAM_LOGO_IMAGE, logoImageBase64);
final RequestDispatcher dispatcher = request.getServletContext()
.getRequestDispatcher(this.guiEntryPoint);
dispatcher.forward(request, response);
// redirect
} else {
request.getSession().removeAttribute(API.PARAM_LOGO_IMAGE);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.sendRedirect(this.guiRedirect);
}
}
private String requestLogoImage(final String requestURI) {
log.debug("Trying to verify insitution from requested entrypoint url: {}", requestURI);
try {
final RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(this.clientHttpRequestFactory);
final ResponseEntity<String> exchange = restTemplate
.exchange(
this.webserviceURIService.getURIBuilder()
.path(API.INFO_ENDPOINT + API.INSTITUTIONAL_LOGO_PATH)
.toUriString(),
HttpMethod.GET,
HttpEntity.EMPTY,
String.class,
requestURI.replaceAll("/", ""));
if (exchange.getStatusCodeValue() == HttpStatus.OK.value()) {
return exchange.getBody();
} else {
log.error("Failed to verify insitution from requested entrypoint url: {}, response: {}", requestURI,
exchange);
}
} catch (final Exception e) {
log.error("Failed to verify insitution from requested entrypoint url: {}", requestURI, e);
}
return null;
}
}

View file

@ -14,7 +14,6 @@ import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.application.AbstractEntryPoint;
import org.eclipse.rap.rwt.application.Application;
@ -22,14 +21,12 @@ import org.eclipse.rap.rwt.application.ApplicationConfiguration;
import org.eclipse.rap.rwt.application.EntryPoint;
import org.eclipse.rap.rwt.application.EntryPointFactory;
import org.eclipse.rap.rwt.client.WebClient;
import org.eclipse.rap.rwt.client.service.StartupParameters;
import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
@ -100,19 +97,6 @@ public class RAPConfiguration implements ApplicationConfiguration {
final HttpSession httpSession,
final WebApplicationContext webApplicationContext) {
// NOTE: if the user comes from a specified institutional login url (redirect from server) the institutionId is get from
// request and put to the session attributes. The institutionId can later be used for institution specific login page
// look and feel as well as for sending the institutionId within the login credentials to give the authorization service
// some restriction to search the user. This is especially useful if the user is external registered and verified
// with LDAP or AAI SAML
final StartupParameters reqParams = RWT.getClient().getService(StartupParameters.class);
final String institutionId = reqParams.getParameter(API.PARAM_INSTITUTION_ID);
if (StringUtils.isNotBlank(institutionId)) {
httpSession.setAttribute(API.PARAM_INSTITUTION_ID, institutionId);
} else {
httpSession.removeAttribute(API.PARAM_INSTITUTION_ID);
}
final AuthorizationContextHolder authorizationContextHolder = webApplicationContext
.getBean(AuthorizationContextHolder.class);
final SEBServerAuthorizationContext authorizationContext = authorizationContextHolder

View file

@ -20,6 +20,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
@ -37,6 +38,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.NewInstitution;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.SaveInstitution;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
@Lazy
@ -46,10 +48,16 @@ public class InstitutionForm implements TemplateComposer {
private final PageFormService pageFormService;
private final RestService restService;
private final CurrentUser currentUser;
protected InstitutionForm(
final PageFormService pageFormService,
final RestService restService,
final CurrentUser currentUser) {
protected InstitutionForm(final PageFormService pageFormService, final RestService restService) {
this.pageFormService = pageFormService;
this.restService = restService;
this.currentUser = currentUser;
}
@Override
@ -93,7 +101,7 @@ public class InstitutionForm implements TemplateComposer {
content.setLayout(contentLayout);
content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
// title
// title (interactive9
final Label pageTitle = widgetFactory.labelLocalizedTitle(
content, new LocTextKey(
"sebserver.institution.form.title",
@ -141,20 +149,24 @@ public class InstitutionForm implements TemplateComposer {
// propagate content actions to action-pane
if (readonly) {
formContext.createAction(ActionDefinition.INSTITUTION_NEW)
.withExec(InstitutionActions::newInstitution)
.publish()
.createAction(ActionDefinition.INSTITUTION_MODIFY)
.withExec(InstitutionActions::editInstitution)
.publish();
if (!institution.isActive()) {
formContext.createAction(ActionDefinition.INSTITUTION_ACTIVATE)
.withExec(InstitutionActions::activateInstitution)
if (this.currentUser.hasPrivilege(PrivilegeType.WRITE, institution)) {
formContext.createAction(ActionDefinition.INSTITUTION_NEW)
.withExec(InstitutionActions::newInstitution)
.publish();
} else {
formContext.createAction(ActionDefinition.INSTITUTION_DEACTIVATE)
.withExec(InstitutionActions::deactivateInstitution)
}
if (this.currentUser.hasPrivilege(PrivilegeType.MODIFY, institution)) {
formContext.createAction(ActionDefinition.INSTITUTION_MODIFY)
.withExec(InstitutionActions::editInstitution)
.publish();
if (!institution.isActive()) {
formContext.createAction(ActionDefinition.INSTITUTION_ACTIVATE)
.withExec(InstitutionActions::activateInstitution)
.publish();
} else {
formContext.createAction(ActionDefinition.INSTITUTION_DEACTIVATE)
.withExec(InstitutionActions::deactivateInstitution)
.publish();
}
}
} else {
formContext.createAction(ActionDefinition.INSTITUTION_SAVE)
@ -164,7 +176,6 @@ public class InstitutionForm implements TemplateComposer {
.withExec(InstitutionActions::cancelEditInstitution)
.withConfirm("sebserver.overall.action.modify.cancel.confirm")
.publish();
}
}

View file

@ -12,6 +12,7 @@ import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import org.apache.commons.codec.binary.Base64InputStream;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
@ -27,7 +28,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
@ -36,7 +36,6 @@ import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService;
import ch.ethz.seb.sebserver.gui.service.widget.Message;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.CustomVariant;
@ -47,20 +46,17 @@ public class DefaultPageLayout implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(DefaultPageLayout.class);
private final WebserviceURIService webserviceURIService;
private final WidgetFactory widgetFactory;
private final PolyglotPageService polyglotPageService;
private final AuthorizationContextHolder authorizationContextHolder;
private final String sebServerVersion;
public DefaultPageLayout(
final WebserviceURIService webserviceURIService,
final WidgetFactory widgetFactory,
final PolyglotPageService polyglotPageService,
final AuthorizationContextHolder authorizationContextHolder,
@Value("${sebserver.version}") final String sebServerVersion) {
this.webserviceURIService = webserviceURIService;
this.widgetFactory = widgetFactory;
this.polyglotPageService = polyglotPageService;
this.authorizationContextHolder = authorizationContextHolder;
@ -173,28 +169,7 @@ public class DefaultPageLayout implements TemplateComposer {
logo.setLayoutData(logoCell);
// try to get institutional logo first. If no success, use default logo
try {
final String institutionId = (String) RWT.getUISession()
.getHttpSession()
.getAttribute(API.PARAM_INSTITUTION_ID);
final String logoBase64 = new RestTemplate()
.getForObject(
this.webserviceURIService.getWebserviceServerAddress() + API.INSTITUTIONAL_LOGO_PATH,
String.class,
institutionId);
final Base64InputStream input = new Base64InputStream(
new ByteArrayInputStream(logoBase64.getBytes(StandardCharsets.UTF_8)),
false);
logo.setData(RWT.CUSTOM_VARIANT, "bgLogoNoImage");
logo.setBackgroundImage(new Image(pageContext.getShell().getDisplay(), input));
} catch (final Exception e) {
log.warn("Get institutional logo failed: {}", e.getMessage());
logo.setData(RWT.CUSTOM_VARIANT, "bgLogo");
}
loadInstitutionalLogo(pageContext, logo);
final Composite langSupport = new Composite(logoBar, SWT.NONE);
final GridData langSupportCell = new GridData(SWT.RIGHT, SWT.CENTER, false, false);
@ -208,14 +183,6 @@ public class DefaultPageLayout implements TemplateComposer {
langSupport.setLayout(rowLayout);
this.widgetFactory.createLanguageSelector(pageContext.copyOf(langSupport));
// for (final Locale locale : this.i18nSupport.supportedLanguages()) {
// final LanguageSelection languageSelection = new LanguageSelection(langSupport, locale);
// languageSelection.updateLocale(this.i18nSupport);
// languageSelection.addListener(SWT.MouseDown, event -> {
// this.polyglotPageService.setPageLocale(pageContext.root, languageSelection.locale);
//
// });
// }
}
private void composeContent(final PageContext pageContext) {
@ -305,25 +272,28 @@ public class DefaultPageLayout implements TemplateComposer {
new LocTextKey("sebserver.overall.version", this.sebServerVersion));
}
// private final class LanguageSelection extends Label implements Polyglot {
//
// private static final long serialVersionUID = 8110167162843383940L;
// private final Locale locale;
//
// public LanguageSelection(final Composite parent, final Locale locale) {
// super(parent, SWT.NONE);
// this.locale = locale;
// super.setData(RWT.CUSTOM_VARIANT, "header");
// super.setText("| " + locale.getLanguage().toUpperCase());
// }
//
// @Override
// public void updateLocale(final I18nSupport i18nSupport) {
// super.setVisible(
// !i18nSupport.getCurrentLocale()
// .getLanguage()
// .equals(this.locale.getLanguage()));
// }
// }
private void loadInstitutionalLogo(final PageContext pageContext, final Composite logo) {
logo.setData(RWT.CUSTOM_VARIANT, "bgLogo");
try {
final String imageBase64 = (String) RWT.getUISession()
.getHttpSession()
.getAttribute(API.PARAM_LOGO_IMAGE);
if (StringUtils.isBlank(imageBase64)) {
return;
}
final Base64InputStream input = new Base64InputStream(
new ByteArrayInputStream(imageBase64.getBytes(StandardCharsets.UTF_8)),
false);
logo.setData(RWT.CUSTOM_VARIANT, "bgLogoNoImage");
logo.setBackgroundImage(new Image(pageContext.getShell().getDisplay(), input));
} catch (final Exception e) {
log.warn("Get institutional logo failed: {}", e.getMessage());
}
}
}

View file

@ -33,12 +33,12 @@ public class RestService {
public RestService(
final AuthorizationContextHolder authorizationContextHolder,
final WebserviceURIService webserviceURIBuilderSupplier,
final JSONMapper jsonMapper,
final Collection<RestCall<?>> calls) {
this.authorizationContextHolder = authorizationContextHolder;
this.webserviceURIBuilderSupplier = webserviceURIBuilderSupplier;
this.webserviceURIBuilderSupplier = authorizationContextHolder
.getWebserviceURIService();
this.calls = calls
.stream()
@ -54,7 +54,7 @@ public class RestService {
}
public UriComponentsBuilder getWebserviceURIBuilder() {
return this.webserviceURIBuilderSupplier.getBuilder();
return this.webserviceURIBuilderSupplier.getURIBuilder();
}
@SuppressWarnings("unchecked")

View file

@ -16,8 +16,11 @@ public interface AuthorizationContextHolder {
SEBServerAuthorizationContext getAuthorizationContext(HttpSession session);
WebserviceURIService getWebserviceURIService();
// TODO error handling!?
default SEBServerAuthorizationContext getAuthorizationContext() {
return getAuthorizationContext(RWT.getUISession().getHttpSession());
}
}

View file

@ -8,17 +8,33 @@
package ch.ethz.seb.sebserver.gui.service.remote.webservice.auth;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.authorization.Privilege;
import ch.ethz.seb.sebserver.gbl.authorization.Privilege.RoleTypeKey;
import ch.ethz.seb.sebserver.gbl.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity;
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
@ -29,6 +45,7 @@ public class CurrentUser {
private final AuthorizationContextHolder authorizationContextHolder;
private SEBServerAuthorizationContext authContext = null;
private Map<RoleTypeKey, Privilege> privileges = null;
public CurrentUser(final AuthorizationContextHolder authorizationContextHolder) {
this.authorizationContextHolder = authorizationContextHolder;
@ -58,6 +75,64 @@ public class CurrentUser {
return null;
}
public boolean hasPrivilege(final PrivilegeType privilegeType, final EntityType entityType) {
if (loadPrivileges()) {
try {
return get().getRoles()
.stream()
.map(roleName -> UserRole.valueOf(roleName))
.map(role -> new RoleTypeKey(entityType, role))
.map(key -> this.privileges.get(key))
.filter(checkPrivilege(get(), privilegeType, null))
.findFirst()
.isPresent();
} catch (final Exception e) {
log.error("Failed to verify privilege: PrivilegeType {} EntityType {}",
privilegeType, entityType, e);
}
}
return false;
}
public boolean hasPrivilege(
final PrivilegeType privilegeType,
final GrantEntity grantEntity) {
if (loadPrivileges()) {
final EntityType entityType = grantEntity.entityType();
try {
return get().getRoles()
.stream()
.map(roleName -> UserRole.valueOf(roleName))
.map(role -> new RoleTypeKey(entityType, role))
.map(key -> this.privileges.get(key))
.filter(checkPrivilege(get(), privilegeType, grantEntity))
.findFirst()
.isPresent();
} catch (final Exception e) {
log.error("Failed to verify privilege: PrivilegeType {} EntityType {}",
privilegeType, entityType, e);
}
}
return false;
}
private Predicate<Privilege> checkPrivilege(
final UserInfo userInfo,
final PrivilegeType privilegeType,
final GrantEntity grantEntity) {
return priv -> priv.hasBasePrivilege(privilegeType)
|| ((grantEntity != null) &&
(priv.hasInstitutionalPrivilege(privilegeType)
&& get().institutionId.longValue() == grantEntity.getInstitutionId()
.longValue())
|| (priv.hasOwnershipPrivilege(privilegeType)
&& get().uuid.equals(grantEntity.getOwnerId())));
}
public boolean isAvailable() {
updateContext();
return this.authContext != null && this.authContext.isLoggedIn();
@ -69,4 +144,52 @@ public class CurrentUser {
}
}
private boolean loadPrivileges() {
if (this.privileges != null) {
return true;
}
updateContext();
if (this.authContext != null) {
try {
final WebserviceURIService webserviceURIService =
this.authorizationContextHolder.getWebserviceURIService();
final ResponseEntity<Collection<Privilege>> exchange = this.authContext.getRestTemplate()
.exchange(
webserviceURIService.getURIBuilder()
.path(API.PRIVILEGES_ENDPOINT)
.toUriString(),
HttpMethod.GET,
HttpEntity.EMPTY,
new ParameterizedTypeReference<Collection<Privilege>>() {
});
if (exchange.getStatusCodeValue() == HttpStatus.OK.value()) {
this.privileges = exchange.getBody().stream()
.reduce(new HashMap<RoleTypeKey, Privilege>(),
(map, priv) -> {
map.put(priv.roleTypeKey, priv);
return map;
},
(map1, map2) -> {
map1.putAll(map2);
return map1;
});
return true;
} else {
log.error("Failed to get Privileges from webservice API: {}", exchange);
return false;
}
} catch (final Exception e) {
log.error("Failed to get Privileges from webservice API: ", e);
return false;
}
} else {
log.error("Failed to get Privileges from webservice API. No AuthorizationContext available");
return false;
}
}
}

View file

@ -75,6 +75,11 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
this.clientHttpRequestFactory = clientHttpRequestFactory;
}
@Override
public WebserviceURIService getWebserviceURIService() {
return this.webserviceURIService;
}
@Override
public SEBServerAuthorizationContext getAuthorizationContext(final HttpSession session) {
log.debug("Trying to get OAuth2AuthorizationContext from HttpSession: {}", session.getId());

View file

@ -43,7 +43,7 @@ public class WebserviceURIService {
return this.webserviceServerAddress;
}
public UriComponentsBuilder getBuilder() {
public UriComponentsBuilder getURIBuilder() {
return this.webserviceURIBuilder.cloneBuilder();
}
@ -60,7 +60,7 @@ public class WebserviceURIService {
}
public String getCurrentUserRequestURI() {
return getBuilder()
return getURIBuilder()
.path(CURRENT_USER_URI_PATH)
.toUriString();
}

View file

@ -9,8 +9,10 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.authorization;
import java.security.Principal;
import java.util.Collection;
import java.util.function.Predicate;
import ch.ethz.seb.sebserver.gbl.authorization.Privilege;
import ch.ethz.seb.sebserver.gbl.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.util.Result;
@ -27,6 +29,11 @@ public interface AuthorizationGrantService {
* @return the UserService that is bundled within the AuthorizationGrantService */
UserService getUserService();
/** All Privileges in a collection.
*
* @return all registered Privileges */
Collection<Privilege> getAllPrivileges();
/** Checks if the current user has any privilege (base or institutional or owner) for the given EntityType and
* PrivilegeType.
*

View file

@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.authorization;
import java.security.Principal;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
@ -21,8 +22,8 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.authorization.Privilege;
import ch.ethz.seb.sebserver.gbl.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.authorization.Privilege.RoleTypeKey;
import ch.ethz.seb.sebserver.gbl.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@ -34,7 +35,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
public class AuthorizationGrantServiceImpl implements AuthorizationGrantService {
/** Map of role based grants for specified entity types. */
private final Map<Privilege.RoleTypeKey, Privilege> grants = new HashMap<>();
private final Map<Privilege.RoleTypeKey, Privilege> privileges = new HashMap<>();
/** Map of collected AuthorizationGrantRule exceptions */
private final Map<EntityType, AuthorizationGrantRule> exceptionalRules =
new EnumMap<>(EntityType.class);
@ -58,6 +59,11 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService
return this.userService;
}
@Override
public Collection<Privilege> getAllPrivileges() {
return Collections.unmodifiableCollection(this.privileges.values());
}
/** Initialize the (hard-coded) grants */
@PostConstruct
public void init() {
@ -200,7 +206,7 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService
final SEBServerUser user) {
for (final UserRole role : user.getUserRoles()) {
final Privilege roleTypeGrant = this.grants.get(new RoleTypeKey(entityType, role));
final Privilege roleTypeGrant = this.privileges.get(new RoleTypeKey(entityType, role));
if (roleTypeGrant != null && roleTypeGrant.hasBasePrivilege(privilegeType)) {
return true;
}
@ -238,7 +244,7 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService
final SEBServerUser user) {
for (final UserRole role : user.getUserRoles()) {
final Privilege roleTypeGrant = this.grants.get(new RoleTypeKey(entityType, role));
final Privilege roleTypeGrant = this.privileges.get(new RoleTypeKey(entityType, role));
if (roleTypeGrant != null && roleTypeGrant.hasInstitutionalPrivilege(privilegeType)) {
return true;
}
@ -328,7 +334,7 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService
this.grants = new EnumMap<>(UserRole.class);
for (final UserRole role : UserRole.values()) {
this.grants.put(role,
service.grants.get(new RoleTypeKey(type, role)));
service.privileges.get(new RoleTypeKey(type, role)));
}
}
@ -437,7 +443,7 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService
this.institutionalPrivilege,
this.ownerPrivilege);
AuthorizationGrantServiceImpl.this.grants.put(roleTypeKey, roleTypeGrant);
AuthorizationGrantServiceImpl.this.privileges.put(roleTypeKey, roleTypeGrant);
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.Collection;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.authorization.Privilege;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO;
@WebServiceProfile
@RestController
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.INFO_ENDPOINT)
public class InfoController {
private final InstitutionDAO institutionDAO;
private final AuthorizationGrantService authorizationGrantService;
protected InfoController(
final InstitutionDAO institutionDAO,
final AuthorizationGrantService authorizationGrantService) {
this.institutionDAO = institutionDAO;
this.authorizationGrantService = authorizationGrantService;
}
@RequestMapping(
path = API.INSTITUTIONAL_LOGO_PATH,
method = RequestMethod.GET,
produces = MediaType.IMAGE_PNG_VALUE + ";base64")
public String logo(@PathVariable final String urlSuffix) {
return this.institutionDAO
.all(null, true)
.getOrThrow()
.stream()
.filter(inst -> urlSuffix.endsWith(inst.urlSuffix))
.findFirst()
.map(inst -> inst.logoImage)
.orElse(null);
}
@RequestMapping(
path = API.PRIVILEGES_PATH_SEGMENT,
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Collection<Privilege> privileges() {
return this.authorizationGrantService.getAllPrivileges();
}
}

View file

@ -1,38 +0,0 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.io.IOException;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO;
@WebServiceProfile
@RestController
public class LogoController {
private final InstitutionDAO institutionDAO;
protected LogoController(final InstitutionDAO institutionDAO) {
this.institutionDAO = institutionDAO;
}
@RequestMapping(API.INSTITUTIONAL_LOGO_PATH)
public String logo(@PathVariable final String institutionId) throws IOException {
return this.institutionDAO
.byModelId(institutionId)
.getOrThrow().logoImage;
}
}

View file

@ -1,19 +0,0 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.api;
import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@WebServiceProfile
@RestController
public class PrivilegesController {
}