Merge pull request #115 from CESNET/merge_controllers
refactor: Merged some controllerspull/1580/head
commit
7877e18f6a
|
@ -65,10 +65,12 @@
|
|||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.ProtectedResourceRegistrationEndpoint).URL}/**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.UserInfoEndpoint).URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.RootController).API_URL}/**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).URL}/**" />
|
||||
<mvc:exclude-mapping path="#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).ENDPOINT_URL}**" />
|
||||
<mvc:exclude-mapping path="#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).REQUEST_USER_CODE_URL}**" />
|
||||
<mvc:exclude-mapping path="#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).DEVICE_APPROVED_URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.IntrospectionEndpoint).URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.RevocationEndpoint).URL}**" />
|
||||
|
||||
|
||||
<!-- Inject the UserInfo into the response -->
|
||||
<ref bean="userInfoInterceptor"/>
|
||||
</mvc:interceptor>
|
||||
|
@ -83,7 +85,7 @@
|
|||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.ProtectedResourceRegistrationEndpoint).URL}/**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.UserInfoEndpoint).URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.RootController).API_URL}/**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).URL}/**" />
|
||||
<mvc:exclude-mapping path="#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).ENDPOINT_URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.IntrospectionEndpoint).URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.RevocationEndpoint).URL}**" />
|
||||
<!-- Inject the server configuration into the response -->
|
||||
|
@ -189,7 +191,7 @@
|
|||
<security:csrf disabled="true"/>
|
||||
</security:http>
|
||||
|
||||
<security:http pattern="/#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).URL}/**"
|
||||
<security:http pattern="#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).ENDPOINT_URL}**"
|
||||
use-expressions="true"
|
||||
entry-point-ref="oauthAuthenticationEntryPoint"
|
||||
create-session="stateless"
|
||||
|
|
|
@ -47,7 +47,9 @@
|
|||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.ProtectedResourceRegistrationEndpoint).URL}/**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.UserInfoEndpoint).URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.RootController).API_URL}/**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).URL}/**" />
|
||||
<mvc:exclude-mapping path="#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).ENDPOINT_URL}**" />
|
||||
<mvc:exclude-mapping path="#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).REQUEST_USER_CODE_URL}**" />
|
||||
<mvc:exclude-mapping path="#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).DEVICE_APPROVED_URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.IntrospectionEndpoint).URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.RevocationEndpoint).URL}**" />
|
||||
<mvc:exclude-mapping path="#{T(cz.muni.ics.oidc.web.controllers.IsTestSpController).MAPPING}**" />
|
||||
|
@ -82,7 +84,7 @@
|
|||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.ProtectedResourceRegistrationEndpoint).URL}/**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.UserInfoEndpoint).URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.openid.connect.web.RootController).API_URL}/**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).URL}/**" />
|
||||
<mvc:exclude-mapping path="#{T(cz.muni.ics.oauth2.web.DeviceEndpoint).ENDPOINT_URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.IntrospectionEndpoint).URL}**" />
|
||||
<mvc:exclude-mapping path="/#{T(cz.muni.ics.oauth2.web.RevocationEndpoint).URL}**" />
|
||||
<!-- Inject the server configuration into the response -->
|
||||
|
@ -495,13 +497,13 @@
|
|||
<!-- SAML -->
|
||||
|
||||
<bean id="clearSessionFilter" class="cz.muni.ics.oidc.saml.SamlInvalidateSessionFilter">
|
||||
<constructor-arg name="pattern" value="/authorize**"/>
|
||||
<constructor-arg name="oidcIssuer" value="${main.oidc.issuer.url}"/>
|
||||
<constructor-arg name="idpEntityId" value="${saml.idp.defaultIdpEntityId}"/>
|
||||
<constructor-arg name="proxySpEntityId" value="${saml.proxy.spEntityId}"/>
|
||||
<constructor-arg name="internalReferrers" value="#{'${saml.internalReferrers}'.split('\s*,\s*')}"/>
|
||||
<constructor-arg name="contextLogoutHandler" ref="logoutHandler"/>
|
||||
</bean>
|
||||
|
||||
<bean id="samlDiscovery" class="org.springframework.security.saml.SAMLDiscovery">
|
||||
<property name="contextProvider" ref="samlContextProvider"/>
|
||||
<property name="samlEntryPoint" ref="samlEntryPoint"/>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
</h1>
|
||||
|
||||
<form name="confirmationForm"
|
||||
action="${ config.issuer }${ config.issuer.endsWith('/') ? '' : '/' }device/approve" method="post">
|
||||
action="${ config.issuer }${ config.issuer.endsWith('/') ? '' : '/' }device/approved" method="post">
|
||||
|
||||
<div class="row">
|
||||
<div class="span5 offset1 well-small" style="text-align: left">
|
||||
|
|
|
@ -39,14 +39,15 @@
|
|||
</c:if>
|
||||
|
||||
|
||||
<form action="${ config.issuer }${ config.issuer.endsWith('/') ? '' : '/' }device/verify" method="POST">
|
||||
<form action="${ config.issuer }${ config.issuer.endsWith('/') ? '' : '/' }device/code" method="POST">
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<spring:message code="device.request_code.submit" var="authorize_label"/>
|
||||
<div>
|
||||
<div class="input-block-level input-xlarge">
|
||||
<input type="text" name="user_code" placeholder="code" autocorrect="off" autocapitalize="off" autocomplete="off" spellcheck="false" value="" />
|
||||
<input type="text" name="user_code" placeholder="code" autocorrect="off"
|
||||
autocapitalize="off" autocomplete="off" spellcheck="false" value="${user_code}" />
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<div id="content">
|
||||
<c:remove scope="session" var="SPRING_SECURITY_LAST_EXCEPTION" />
|
||||
<form name="confirmationForm"
|
||||
action="${ config.issuer }${ config.issuer.endsWith('/') ? '' : '/' }device/approve" method="post">
|
||||
action="${ config.issuer }${ config.issuer.endsWith('/') ? '' : '/' }device/approved" method="post">
|
||||
<p>
|
||||
<c:if test="${not empty client.policyUri}">
|
||||
<spring:message code="device_approve_privacy"/>${" "}<a target='_blank' href='${fn:escapeXml(client.policyUri)}'><em>${fn:escapeXml(client.clientName)}</em></a>
|
||||
|
|
|
@ -43,7 +43,7 @@ pageContext.setAttribute("cssLinks", cssLinks);
|
|||
</c:if>
|
||||
${". "}<spring:message code="device_approved_text_rejected_end"/>
|
||||
</c:if>
|
||||
</p>q
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div> <%-- wrap --%>
|
||||
|
|
|
@ -51,15 +51,15 @@
|
|||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<form name="confirmationForm" class="mt-2" method="post"
|
||||
action="${ config.issuer }${ config.issuer.endsWith('/') ? '' : '/' }device/verify">
|
||||
<form name="confirmationForm" class="mt-2" method="POST"
|
||||
action="${ config.issuer }${ config.issuer.endsWith('/') ? '' : '/' }device/code">
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div>
|
||||
<div class="input-block-level input-xlarge">
|
||||
<spring:message code="code" var="code_placeholder"/>
|
||||
<input type="text" name="user_code" placeholder="${code_placeholder}"
|
||||
autocapitalize="off" autocomplete="off" spellcheck="false" value="" />
|
||||
autocapitalize="off" autocomplete="off" spellcheck="false" value="${user_code}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -346,7 +346,7 @@ public class DiscoveryEndpoint {
|
|||
|
||||
m.put("code_challenge_methods_supported", Lists.newArrayList(PKCEAlgorithm.plain.getName(), PKCEAlgorithm.S256.getName()));
|
||||
|
||||
m.put("device_authorization_endpoint", baseUrl + DeviceEndpoint.URL);
|
||||
m.put("device_authorization_endpoint", config.getIssuer(false) + DeviceEndpoint.ENDPOINT_URL);
|
||||
|
||||
model.addAttribute(JsonEntityView.ENTITY, m);
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package cz.muni.ics.oauth2.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ClientWithScopes {
|
||||
|
||||
private ClientDetailsEntity client;
|
||||
private Set<String> requestedScopes;
|
||||
|
||||
}
|
|
@ -16,27 +16,31 @@
|
|||
|
||||
package cz.muni.ics.oauth2.web;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import cz.muni.ics.oauth2.exception.DeviceCodeCreationException;
|
||||
import cz.muni.ics.oauth2.model.ClientDetailsEntity;
|
||||
import cz.muni.ics.oauth2.model.ClientWithScopes;
|
||||
import cz.muni.ics.oauth2.model.DeviceCode;
|
||||
import cz.muni.ics.oauth2.model.SystemScope;
|
||||
import cz.muni.ics.oauth2.service.ClientDetailsEntityService;
|
||||
import cz.muni.ics.oauth2.service.DeviceCodeService;
|
||||
import cz.muni.ics.oauth2.service.SystemScopeService;
|
||||
import cz.muni.ics.oauth2.token.DeviceTokenGranter;
|
||||
import cz.muni.ics.openid.connect.config.ConfigurationPropertiesBean;
|
||||
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
||||
import cz.muni.ics.oidc.server.userInfo.PerunUserInfo;
|
||||
import cz.muni.ics.oidc.web.WebHtmlClasses;
|
||||
import cz.muni.ics.oidc.web.controllers.ControllerUtils;
|
||||
import cz.muni.ics.openid.connect.service.ScopeClaimTranslationService;
|
||||
import cz.muni.ics.openid.connect.service.UserInfoService;
|
||||
import cz.muni.ics.openid.connect.view.HttpCodeView;
|
||||
import cz.muni.ics.openid.connect.view.JsonEntityView;
|
||||
import cz.muni.ics.openid.connect.view.JsonErrorView;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.Principal;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
|
@ -46,6 +50,7 @@ import org.springframework.http.MediaType;
|
|||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
|
||||
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||
import org.springframework.security.oauth2.common.util.OAuth2Utils;
|
||||
import org.springframework.security.oauth2.provider.AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
|
@ -53,9 +58,11 @@ import org.springframework.security.oauth2.provider.OAuth2Request;
|
|||
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
/**
|
||||
* Implements https://tools.ietf.org/html/draft-ietf-oauth-device-flow
|
||||
|
@ -63,53 +70,275 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||
* @see DeviceTokenGranter
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
* @author Dominik Baranek (baranek@ics.muni.cz)
|
||||
*/
|
||||
@Controller
|
||||
@Slf4j
|
||||
public class DeviceEndpoint {
|
||||
|
||||
public static final String URL = "devicecode";
|
||||
public static final String USER_URL = "device";
|
||||
// views
|
||||
public static final String REQUEST_USER_CODE = "requestUserCode";
|
||||
public static final String THEMED_REQUEST_USER_CODE = "themedRequestUserCode";
|
||||
public static final String APPROVE_DEVICE = "approveDevice";
|
||||
public static final String THEMED_APPROVE_DEVICE = "themedApproveDevice";
|
||||
public static final String DEVICE_APPROVED = "deviceApproved";
|
||||
public static final String THEMED_DEVICE_APPROVED = "themedDeviceApproved";
|
||||
|
||||
// response keys
|
||||
public static final String DEVICE_CODE = "device_code";
|
||||
public static final String USER_CODE = "user_code";
|
||||
public static final String VERIFICATION_URI_COMPLETE = "verification_uri_complete";
|
||||
public static final String EXPIRES_IN = "expires_in";
|
||||
public static final String VERIFICATION_URI = "verification_uri";
|
||||
|
||||
// request params
|
||||
public static final String CLIENT_ID = "client_id";
|
||||
public static final String USER_OAUTH_APPROVAL = "user_oauth_approval";
|
||||
public static final String SCOPE = "scope";
|
||||
public static final String ACR_VALUES = "acr_values";
|
||||
|
||||
// model keys
|
||||
public static final String CLIENT = "client";
|
||||
public static final String DC = "dc";
|
||||
public static final String APPROVED = "approved";
|
||||
public static final String SCOPES = "scopes";
|
||||
public static final String PAGE = "page";
|
||||
public static final String ERROR = "error";
|
||||
|
||||
// session attributes
|
||||
public static final String DEVICE_CODE_SESSION_ATTRIBUTE = "deviceCode";
|
||||
public static final String AUTHORIZATION_REQUEST = "authorizationRequest";
|
||||
|
||||
// errors
|
||||
public static final String NO_USER_CODE = "noUserCode";
|
||||
public static final String EXPIRED_USER_CODE = "expiredUserCode";
|
||||
public static final String USER_CODE_ALREADY_APPROVED = "userCodeAlreadyApproved";
|
||||
public static final String USER_CODE_MISMATCH = "userCodeMismatch";
|
||||
public static final String INVALID_SCOPE = "invalidScope";
|
||||
|
||||
// other
|
||||
public static final String DEFAULT = "default";
|
||||
public static final String ENDPOINT_URL = "/devicecode";
|
||||
public static final String REQUEST_USER_CODE_URL = "/device/code";
|
||||
public static final String CHECK_USER_CODE_URL = "/device/checkcode";
|
||||
public static final String DEVICE_APPROVED_URL = "/device/approved";
|
||||
|
||||
private final ClientDetailsEntityService clientService;
|
||||
private final SystemScopeService scopeService;
|
||||
private final DeviceCodeService deviceCodeService;
|
||||
private final OAuth2RequestFactory oAuth2RequestFactory;
|
||||
private final PerunOidcConfig perunOidcConfig;
|
||||
private final WebHtmlClasses htmlClasses;
|
||||
private final ScopeClaimTranslationService scopeClaimTranslationService;
|
||||
private final UserInfoService userInfoService;
|
||||
|
||||
@Autowired
|
||||
private ClientDetailsEntityService clientService;
|
||||
public DeviceEndpoint(ClientDetailsEntityService clientService,
|
||||
SystemScopeService scopeService,
|
||||
DeviceCodeService deviceCodeService,
|
||||
OAuth2RequestFactory oAuth2RequestFactory,
|
||||
PerunOidcConfig perunOidcConfig,
|
||||
WebHtmlClasses htmlClasses,
|
||||
ScopeClaimTranslationService scopeClaimTranslationService,
|
||||
UserInfoService userInfoService)
|
||||
{
|
||||
this.clientService = clientService;
|
||||
this.scopeService = scopeService;
|
||||
this.deviceCodeService = deviceCodeService;
|
||||
this.oAuth2RequestFactory = oAuth2RequestFactory;
|
||||
this.perunOidcConfig = perunOidcConfig;
|
||||
this.htmlClasses = htmlClasses;
|
||||
this.scopeClaimTranslationService = scopeClaimTranslationService;
|
||||
this.userInfoService = userInfoService;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private SystemScopeService scopeService;
|
||||
@PostMapping(value = ENDPOINT_URL, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String requestDeviceCode(@RequestParam(CLIENT_ID) String clientId,
|
||||
@RequestParam(name = SCOPE, required = false) String scope,
|
||||
@RequestParam(name = ACR_VALUES, required = false) String acrValues,
|
||||
Map<String, String> parameters,
|
||||
ModelMap model)
|
||||
{
|
||||
ClientWithScopes clientWithScopes = new ClientWithScopes();
|
||||
String errorViewName = preprocessRequest(clientId, clientWithScopes, scope, model);
|
||||
|
||||
@Autowired
|
||||
private ConfigurationPropertiesBean config;
|
||||
if (errorViewName != null) {
|
||||
return errorViewName;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private DeviceCodeService deviceCodeService;
|
||||
try {
|
||||
DeviceCode dc = deviceCodeService.createNewDeviceCode(
|
||||
clientWithScopes.getRequestedScopes(),
|
||||
clientWithScopes.getClient(),
|
||||
parameters
|
||||
);
|
||||
|
||||
@Autowired
|
||||
private OAuth2RequestFactory oAuth2RequestFactory;
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
@RequestMapping(value = "/" + URL, method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String requestDeviceCode(@RequestParam("client_id") String clientId, @RequestParam(name="scope", required=false) String scope, Map<String, String> parameters, ModelMap model) {
|
||||
response.put(DEVICE_CODE, dc.getDeviceCode());
|
||||
response.put(USER_CODE, dc.getUserCode());
|
||||
|
||||
Map<String, String> uriParams = new HashMap<>();
|
||||
if (StringUtils.hasText(acrValues)) {
|
||||
uriParams.put(ACR_VALUES, acrValues);
|
||||
}
|
||||
String uriBase = perunOidcConfig.getConfigBean().getIssuer(false) + REQUEST_USER_CODE_URL;
|
||||
response.put(VERIFICATION_URI, constructVerificationURI(uriBase, uriParams));
|
||||
|
||||
if (perunOidcConfig.getConfigBean().isAllowCompleteDeviceCodeUri()) {
|
||||
uriParams.put(USER_CODE, dc.getUserCode());
|
||||
response.put(VERIFICATION_URI_COMPLETE, constructVerificationURI(uriBase, uriParams));
|
||||
}
|
||||
|
||||
if (clientWithScopes.getClient().getDeviceCodeValiditySeconds() != null) {
|
||||
response.put(EXPIRES_IN, clientWithScopes.getClient().getDeviceCodeValiditySeconds());
|
||||
}
|
||||
|
||||
model.put(JsonEntityView.ENTITY, response);
|
||||
return JsonEntityView.VIEWNAME;
|
||||
} catch (DeviceCodeCreationException ex) {
|
||||
model.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||
model.put(JsonErrorView.ERROR, ex.getError());
|
||||
model.put(JsonErrorView.ERROR_MESSAGE, ex.getMessage());
|
||||
return JsonErrorView.VIEWNAME;
|
||||
} catch (URISyntaxException ex) {
|
||||
log.error("unable to build verification_uri_complete due to wrong syntax of uri components");
|
||||
model.put(HttpCodeView.CODE, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
return HttpCodeView.VIEWNAME;
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@GetMapping(value = REQUEST_USER_CODE_URL)
|
||||
public String requestUserCode(@RequestParam(value = USER_CODE, required = false) String userCode,
|
||||
HttpServletRequest req,
|
||||
ModelMap model)
|
||||
{
|
||||
if (perunOidcConfig.getConfigBean().isAllowCompleteDeviceCodeUri() && StringUtils.hasText(userCode)) {
|
||||
return verifyUserCode(userCode, req, model);
|
||||
} else {
|
||||
return getRequestUserCodeViewName(req, model);
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@PostMapping(value = REQUEST_USER_CODE_URL)
|
||||
public String verifyUserCode(@RequestParam(value = USER_CODE) String userCode,
|
||||
HttpServletRequest req,
|
||||
ModelMap model)
|
||||
{
|
||||
model.put(USER_CODE, userCode);
|
||||
|
||||
DeviceCode dc = deviceCodeService.lookUpByUserCode(userCode);
|
||||
|
||||
String errorViewName = checkDeviceCodeIsValid(dc, req, model);
|
||||
if (errorViewName != null) {
|
||||
return errorViewName;
|
||||
}
|
||||
|
||||
HttpSession session = req.getSession();
|
||||
session.setAttribute(DEVICE_CODE_SESSION_ATTRIBUTE, dc);
|
||||
|
||||
return "redirect:" + CHECK_USER_CODE_URL + '?' + USER_CODE + '=' + userCode;
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@GetMapping(value = CHECK_USER_CODE_URL)
|
||||
public String startApproveDevice(@RequestParam(USER_CODE) String userCode,
|
||||
ModelMap model,
|
||||
Principal p,
|
||||
HttpServletRequest req)
|
||||
{
|
||||
DeviceCode dc = deviceCodeService.lookUpByUserCode(userCode);
|
||||
model.put(USER_CODE, userCode);
|
||||
|
||||
String errorViewName = checkDeviceCodeIsValid(dc, req, model);
|
||||
if (errorViewName != null) {
|
||||
return errorViewName;
|
||||
}
|
||||
|
||||
ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId());
|
||||
|
||||
model.put(CLIENT, client);
|
||||
model.put(DC, dc);
|
||||
|
||||
HttpSession session = req.getSession();
|
||||
AuthorizationRequest authorizationRequest = oAuth2RequestFactory.createAuthorizationRequest(dc.getRequestParameters());
|
||||
session.setAttribute(AUTHORIZATION_REQUEST, authorizationRequest);
|
||||
|
||||
return getApproveDeviceViewName(model, p, req, dc);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@PostMapping(value = DEVICE_APPROVED_URL)
|
||||
public String processApproveDevice(@RequestParam(USER_CODE) String userCode,
|
||||
@RequestParam(value = USER_OAUTH_APPROVAL) Boolean approve,
|
||||
Principal p,
|
||||
HttpServletRequest req,
|
||||
ModelMap model,
|
||||
Authentication auth)
|
||||
{
|
||||
HttpSession session = req.getSession();
|
||||
AuthorizationRequest authorizationRequest = (AuthorizationRequest) session.getAttribute(AUTHORIZATION_REQUEST);
|
||||
if (authorizationRequest == null) {
|
||||
throw new IllegalArgumentException("Authorization request not found in the session");
|
||||
}
|
||||
|
||||
DeviceCode dc = (DeviceCode) session.getAttribute(DEVICE_CODE_SESSION_ATTRIBUTE);
|
||||
if (dc == null) {
|
||||
throw new IllegalArgumentException("Device code not found in the session");
|
||||
}
|
||||
|
||||
// make sure the form that was submitted is the one that we were expecting
|
||||
if (!dc.getUserCode().equals(userCode)) {
|
||||
model.addAttribute(ERROR, USER_CODE_MISMATCH);
|
||||
return REQUEST_USER_CODE;
|
||||
}
|
||||
|
||||
// make sure the code hasn't expired yet
|
||||
if (dc.getExpiration() != null && dc.getExpiration().before(new Date())) {
|
||||
model.addAttribute(ERROR, EXPIRED_USER_CODE);
|
||||
return REQUEST_USER_CODE;
|
||||
}
|
||||
|
||||
ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId());
|
||||
model.put(CLIENT, client);
|
||||
|
||||
// user did not approve
|
||||
if (!approve) {
|
||||
model.addAttribute(APPROVED, false);
|
||||
return getApproveDeviceViewName(model, p, req, dc);
|
||||
}
|
||||
|
||||
// create an OAuth request for storage
|
||||
OAuth2Request o2req = oAuth2RequestFactory.createOAuth2Request(authorizationRequest);
|
||||
OAuth2Authentication o2Auth = new OAuth2Authentication(o2req, auth);
|
||||
|
||||
deviceCodeService.approveDeviceCode(dc, o2Auth);
|
||||
|
||||
model.put(APPROVED, true);
|
||||
return getDeviceApprovedViewName(req, model);
|
||||
}
|
||||
|
||||
private String preprocessRequest(String clientId, ClientWithScopes clientWithScopes, String scope, ModelMap model) {
|
||||
ClientDetailsEntity client;
|
||||
|
||||
try {
|
||||
client = clientService.loadClientByClientId(clientId);
|
||||
|
||||
// make sure this client can do the device flow
|
||||
|
||||
Collection<String> authorizedGrantTypes = client.getAuthorizedGrantTypes();
|
||||
if (authorizedGrantTypes != null && !authorizedGrantTypes.isEmpty()
|
||||
|
||||
if (authorizedGrantTypes != null
|
||||
&& !authorizedGrantTypes.isEmpty()
|
||||
&& !authorizedGrantTypes.contains(DeviceTokenGranter.GRANT_TYPE)) {
|
||||
throw new InvalidClientException("Unauthorized grant type: " + DeviceTokenGranter.GRANT_TYPE);
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.error("IllegalArgumentException was thrown when attempting to load client", e);
|
||||
model.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||
return HttpCodeView.VIEWNAME;
|
||||
}
|
||||
|
||||
if (client == null) {
|
||||
log.error("could not find client " + clientId);
|
||||
} catch (OAuth2Exception e) {
|
||||
log.error("could not find client {}", clientId);
|
||||
model.put(HttpCodeView.CODE, HttpStatus.NOT_FOUND);
|
||||
return HttpCodeView.VIEWNAME;
|
||||
}
|
||||
|
@ -119,183 +348,89 @@ public class DeviceEndpoint {
|
|||
Set<String> allowedScopes = client.getScope();
|
||||
|
||||
if (!scopeService.scopesMatch(allowedScopes, requestedScopes)) {
|
||||
// client asked for scopes it can't have
|
||||
log.error("Client asked for " + requestedScopes + " but is allowed " + allowedScopes);
|
||||
log.error("Client asked for {} but is allowed to request only {}", requestedScopes, allowedScopes);
|
||||
model.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||
model.put(JsonErrorView.ERROR, "invalid_scope");
|
||||
model.put(JsonErrorView.ERROR, INVALID_SCOPE);
|
||||
return JsonErrorView.VIEWNAME;
|
||||
}
|
||||
|
||||
// if we got here the request is legit
|
||||
|
||||
try {
|
||||
DeviceCode dc = deviceCodeService.createNewDeviceCode(requestedScopes, client, parameters);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("device_code", dc.getDeviceCode());
|
||||
response.put("user_code", dc.getUserCode());
|
||||
response.put("verification_uri", config.getIssuer() + USER_URL);
|
||||
if (client.getDeviceCodeValiditySeconds() != null) {
|
||||
response.put("expires_in", client.getDeviceCodeValiditySeconds());
|
||||
}
|
||||
|
||||
if (config.isAllowCompleteDeviceCodeUri()) {
|
||||
URI verificationUriComplete = new URIBuilder(config.getIssuer() + USER_URL)
|
||||
.addParameter("user_code", dc.getUserCode())
|
||||
.build();
|
||||
|
||||
response.put("verification_uri_complete", verificationUriComplete.toString());
|
||||
}
|
||||
|
||||
model.put(JsonEntityView.ENTITY, response);
|
||||
|
||||
|
||||
return JsonEntityView.VIEWNAME;
|
||||
} catch (DeviceCodeCreationException dcce) {
|
||||
|
||||
model.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||
model.put(JsonErrorView.ERROR, dcce.getError());
|
||||
model.put(JsonErrorView.ERROR_MESSAGE, dcce.getMessage());
|
||||
|
||||
return JsonErrorView.VIEWNAME;
|
||||
} catch (URISyntaxException use) {
|
||||
log.error("unable to build verification_uri_complete due to wrong syntax of uri components");
|
||||
model.put(HttpCodeView.CODE, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
|
||||
return HttpCodeView.VIEWNAME;
|
||||
}
|
||||
clientWithScopes.setClient(client);
|
||||
clientWithScopes.setRequestedScopes(requestedScopes);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@RequestMapping(value = "/" + USER_URL, method = RequestMethod.GET)
|
||||
public String requestUserCode(@RequestParam(value = "user_code", required = false) String userCode, ModelMap model, HttpSession session) {
|
||||
|
||||
if (!config.isAllowCompleteDeviceCodeUri() || userCode == null) {
|
||||
// if we don't allow the complete URI or we didn't get a user code on the way in,
|
||||
// print out a page that asks the user to enter their user code
|
||||
// user must be logged in
|
||||
return "requestUserCode";
|
||||
} else {
|
||||
|
||||
// complete verification uri was used, we received user code directly
|
||||
// skip requesting code page
|
||||
// user must be logged in
|
||||
return readUserCode(userCode, model, session);
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@RequestMapping(value = "/" + USER_URL + "/verify", method = RequestMethod.POST)
|
||||
public String readUserCode(@RequestParam("user_code") String userCode, ModelMap model, HttpSession session) {
|
||||
|
||||
// look up the request based on the user code
|
||||
DeviceCode dc = deviceCodeService.lookUpByUserCode(userCode);
|
||||
|
||||
private String checkDeviceCodeIsValid(DeviceCode dc, HttpServletRequest req, ModelMap model) {
|
||||
// we couldn't find the device code
|
||||
if (dc == null) {
|
||||
model.addAttribute("error", "noUserCode");
|
||||
return "requestUserCode";
|
||||
model.addAttribute(ERROR, NO_USER_CODE);
|
||||
return getRequestUserCodeViewName(req, model);
|
||||
}
|
||||
|
||||
// make sure the code hasn't expired yet
|
||||
if (dc.getExpiration() != null && dc.getExpiration().before(new Date())) {
|
||||
model.addAttribute("error", "expiredUserCode");
|
||||
return "requestUserCode";
|
||||
model.addAttribute(ERROR, EXPIRED_USER_CODE);
|
||||
return getRequestUserCodeViewName(req, model);
|
||||
}
|
||||
|
||||
// make sure the device code hasn't already been approved
|
||||
if (dc.isApproved()) {
|
||||
model.addAttribute("error", "userCodeAlreadyApproved");
|
||||
return "requestUserCode";
|
||||
model.addAttribute(ERROR, USER_CODE_ALREADY_APPROVED);
|
||||
return getRequestUserCodeViewName(req, model);
|
||||
}
|
||||
|
||||
ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId());
|
||||
|
||||
model.put("client", client);
|
||||
model.put("dc", dc);
|
||||
|
||||
// pre-process the scopes
|
||||
Set<SystemScope> scopes = scopeService.fromStrings(dc.getScope());
|
||||
|
||||
Set<SystemScope> sortedScopes = new LinkedHashSet<>(scopes.size());
|
||||
Set<SystemScope> systemScopes = scopeService.getAll();
|
||||
|
||||
// sort scopes for display based on the inherent order of system scopes
|
||||
for (SystemScope s : systemScopes) {
|
||||
if (scopes.contains(s)) {
|
||||
sortedScopes.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
// add in any scopes that aren't system scopes to the end of the list
|
||||
sortedScopes.addAll(Sets.difference(scopes, systemScopes));
|
||||
|
||||
model.put("scopes", sortedScopes);
|
||||
|
||||
AuthorizationRequest authorizationRequest = oAuth2RequestFactory.createAuthorizationRequest(dc.getRequestParameters());
|
||||
|
||||
session.setAttribute("authorizationRequest", authorizationRequest);
|
||||
session.setAttribute("deviceCode", dc);
|
||||
|
||||
return "approveDevice";
|
||||
return null;
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@RequestMapping(value = "/" + USER_URL + "/approve", method = RequestMethod.POST)
|
||||
public String approveDevice(@RequestParam("user_code") String userCode,
|
||||
@RequestParam(value = "user_oauth_approval") Boolean approve,
|
||||
ModelMap model, Authentication auth, HttpSession session) {
|
||||
AuthorizationRequest authorizationRequest = (AuthorizationRequest) session.getAttribute("authorizationRequest");
|
||||
DeviceCode dc = (DeviceCode) session.getAttribute("deviceCode");
|
||||
|
||||
// make sure the form that was submitted is the one that we were expecting
|
||||
if (!dc.getUserCode().equals(userCode)) {
|
||||
model.addAttribute("error", "userCodeMismatch");
|
||||
return "requestUserCode";
|
||||
private String getRequestUserCodeViewName(HttpServletRequest req, ModelMap model) {
|
||||
if (perunOidcConfig.getTheme().equalsIgnoreCase(DEFAULT)) {
|
||||
return REQUEST_USER_CODE;
|
||||
}
|
||||
|
||||
// make sure the code hasn't expired yet
|
||||
if (dc.getExpiration() != null && dc.getExpiration().before(new Date())) {
|
||||
model.addAttribute("error", "expiredUserCode");
|
||||
return "requestUserCode";
|
||||
ControllerUtils.setPageOptions(model, req, htmlClasses, perunOidcConfig);
|
||||
model.put(PAGE, REQUEST_USER_CODE);
|
||||
return THEMED_REQUEST_USER_CODE;
|
||||
}
|
||||
|
||||
private String getDeviceApprovedViewName(HttpServletRequest req, ModelMap model) {
|
||||
if (perunOidcConfig.getTheme().equalsIgnoreCase(DEFAULT)) {
|
||||
return DEVICE_APPROVED;
|
||||
}
|
||||
|
||||
ClientDetailsEntity client = clientService.loadClientByClientId(dc.getClientId());
|
||||
ControllerUtils.setPageOptions(model, req, htmlClasses, perunOidcConfig);
|
||||
model.put(PAGE, DEVICE_APPROVED);
|
||||
return THEMED_DEVICE_APPROVED;
|
||||
}
|
||||
|
||||
model.put("client", client);
|
||||
|
||||
// user did not approve
|
||||
if (!approve) {
|
||||
model.addAttribute("approved", false);
|
||||
return "deviceApproved";
|
||||
private String getApproveDeviceViewName(ModelMap model, Principal p, HttpServletRequest req, DeviceCode dc) {
|
||||
if (perunOidcConfig.getTheme().equalsIgnoreCase(DEFAULT)) {
|
||||
model.put(SCOPES, ControllerUtils.getSortedScopes(dc.getScope(), scopeService));
|
||||
return APPROVE_DEVICE;
|
||||
}
|
||||
|
||||
// create an OAuth request for storage
|
||||
OAuth2Request o2req = oAuth2RequestFactory.createOAuth2Request(authorizationRequest);
|
||||
OAuth2Authentication o2Auth = new OAuth2Authentication(o2req, auth);
|
||||
|
||||
DeviceCode approvedCode = deviceCodeService.approveDeviceCode(dc, o2Auth);
|
||||
|
||||
// pre-process the scopes
|
||||
Set<SystemScope> scopes = scopeService.fromStrings(dc.getScope());
|
||||
ClientDetailsEntity client = (ClientDetailsEntity) model.get(CLIENT);
|
||||
|
||||
Set<SystemScope> sortedScopes = new LinkedHashSet<>(scopes.size());
|
||||
Set<SystemScope> systemScopes = scopeService.getAll();
|
||||
PerunUserInfo user = (PerunUserInfo) userInfoService.getByUsernameAndClientId(
|
||||
p.getName(),
|
||||
client.getClientId()
|
||||
);
|
||||
|
||||
// sort scopes for display based on the inherent order of system scopes
|
||||
for (SystemScope s : systemScopes) {
|
||||
if (scopes.contains(s)) {
|
||||
sortedScopes.add(s);
|
||||
}
|
||||
ControllerUtils.setScopesAndClaims(scopeService, scopeClaimTranslationService, model, dc.getScope(), user);
|
||||
ControllerUtils.setPageOptions(model, req, htmlClasses, perunOidcConfig);
|
||||
|
||||
model.put(PAGE, APPROVE_DEVICE);
|
||||
return THEMED_APPROVE_DEVICE;
|
||||
}
|
||||
|
||||
private String constructVerificationURI(String uri, Map<String, String> params) throws URISyntaxException {
|
||||
if (params == null || params.isEmpty()) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
// add in any scopes that aren't system scopes to the end of the list
|
||||
sortedScopes.addAll(Sets.difference(scopes, systemScopes));
|
||||
|
||||
model.put("scopes", sortedScopes);
|
||||
model.put("approved", true);
|
||||
|
||||
return "deviceApproved";
|
||||
URIBuilder builder = new URIBuilder(uri);
|
||||
for (Map.Entry<String, String> param: params.entrySet()) {
|
||||
builder.addParameter(param.getKey(), param.getValue());
|
||||
}
|
||||
return builder.build().toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ import cz.muni.ics.oauth2.model.ClientDetailsEntity;
|
|||
import cz.muni.ics.oauth2.model.SystemScope;
|
||||
import cz.muni.ics.oauth2.service.ClientDetailsEntityService;
|
||||
import cz.muni.ics.oauth2.service.SystemScopeService;
|
||||
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
||||
import cz.muni.ics.oidc.server.userInfo.PerunUserInfo;
|
||||
import cz.muni.ics.oidc.web.WebHtmlClasses;
|
||||
import cz.muni.ics.oidc.web.controllers.ControllerUtils;
|
||||
import cz.muni.ics.openid.connect.model.UserInfo;
|
||||
import cz.muni.ics.openid.connect.request.ConnectRequestParameters;
|
||||
import cz.muni.ics.openid.connect.service.ScopeClaimTranslationService;
|
||||
|
@ -41,6 +45,9 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -53,6 +60,8 @@ import org.springframework.stereotype.Controller;
|
|||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
|
@ -62,20 +71,54 @@ import org.springframework.web.bind.annotation.SessionAttributes;
|
|||
@Slf4j
|
||||
public class OAuthConfirmationController {
|
||||
|
||||
@Autowired
|
||||
public static final String AUTHORIZATION_REQUEST = "authorizationRequest";
|
||||
public static final String ERROR = "error";
|
||||
public static final String INTERACTION_REQUIRED = "interaction_required";
|
||||
public static final String STATE = "state";
|
||||
public static final String NONE = "none";
|
||||
public static final String REDIRECT = "redirect";
|
||||
public static final String CODE = "code";
|
||||
public static final String AUTH_REQUEST = "auth_request";
|
||||
public static final String CLIENT = "client";
|
||||
public static final String REDIRECT_URI = "redirect_uri";
|
||||
public static final String SCOPES = "scopes";
|
||||
public static final String CLAIMS = "claims";
|
||||
public static final String CONTACTS = "contacts";
|
||||
public static final String GRAS = "gras";
|
||||
public static final String DEFAULT = "default";
|
||||
public static final String PAGE = "page";
|
||||
public static final String CONSENT = "consent";
|
||||
public static final String THEMED_APPROVE = "themedApprove";
|
||||
public static final String APPROVE = "approve";
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private ClientDetailsEntityService clientService;
|
||||
|
||||
@Autowired
|
||||
private SystemScopeService scopeService;
|
||||
|
||||
@Autowired
|
||||
private ScopeClaimTranslationService scopeClaimTranslationService;
|
||||
|
||||
@Autowired
|
||||
private UserInfoService userInfoService;
|
||||
private RedirectResolver redirectResolver;
|
||||
private PerunOidcConfig perunOidcConfig;
|
||||
private WebHtmlClasses htmlClasses;
|
||||
|
||||
@Autowired
|
||||
private RedirectResolver redirectResolver;
|
||||
public OAuthConfirmationController(ClientDetailsEntityService clientService,
|
||||
SystemScopeService scopeService,
|
||||
ScopeClaimTranslationService scopeClaimTranslationService,
|
||||
UserInfoService userInfoService,
|
||||
RedirectResolver redirectResolver,
|
||||
PerunOidcConfig perunOidcConfig,
|
||||
WebHtmlClasses htmlClasses) {
|
||||
|
||||
this.clientService = clientService;
|
||||
this.scopeService = scopeService;
|
||||
this.scopeClaimTranslationService = scopeClaimTranslationService;
|
||||
this.userInfoService = userInfoService;
|
||||
this.redirectResolver = redirectResolver;
|
||||
this.perunOidcConfig = perunOidcConfig;
|
||||
this.htmlClasses = htmlClasses;
|
||||
}
|
||||
|
||||
public OAuthConfirmationController() {
|
||||
|
||||
|
@ -87,9 +130,9 @@ public class OAuthConfirmationController {
|
|||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@RequestMapping("/oauth/confirm_access")
|
||||
public String confirmAccess(Map<String, Object> model, Principal p) {
|
||||
public String confirmAccess(Map<String, Object> model, HttpServletRequest req, Principal p) {
|
||||
|
||||
AuthorizationRequest authRequest = (AuthorizationRequest) model.get("authorizationRequest");
|
||||
AuthorizationRequest authRequest = (AuthorizationRequest) model.get(AUTHORIZATION_REQUEST);
|
||||
// Check the "prompt" parameter to see if we need to do special processing
|
||||
|
||||
String prompt = (String)authRequest.getExtensions().get(ConnectRequestParameters.PROMPT);
|
||||
|
@ -114,57 +157,65 @@ public class OAuthConfirmationController {
|
|||
return HttpCodeView.VIEWNAME;
|
||||
}
|
||||
|
||||
if (prompts.contains("none")) {
|
||||
if (prompts.contains(NONE)) {
|
||||
// if we've got a redirect URI then we'll send it
|
||||
|
||||
String url = redirectResolver.resolveRedirect(authRequest.getRedirectUri(), client);
|
||||
|
||||
try {
|
||||
URIBuilder uriBuilder = new URIBuilder(url);
|
||||
|
||||
uriBuilder.addParameter("error", "interaction_required");
|
||||
if (!Strings.isNullOrEmpty(authRequest.getState())) {
|
||||
uriBuilder.addParameter("state", authRequest.getState()); // copy the state parameter if one was given
|
||||
}
|
||||
|
||||
return "redirect:" + uriBuilder;
|
||||
|
||||
} catch (URISyntaxException e) {
|
||||
log.error("Can't build redirect URI for prompt=none, sending error instead", e);
|
||||
model.put("code", HttpStatus.FORBIDDEN);
|
||||
return HttpCodeView.VIEWNAME;
|
||||
}
|
||||
return sendRedirect(authRequest, model, client);
|
||||
}
|
||||
|
||||
model.put("auth_request", authRequest);
|
||||
model.put("client", client);
|
||||
model.put(AUTH_REQUEST, authRequest);
|
||||
model.put(CLIENT, client);
|
||||
model.put(REDIRECT_URI, authRequest.getRedirectUri());
|
||||
|
||||
String redirect_uri = authRequest.getRedirectUri();
|
||||
|
||||
model.put("redirect_uri", redirect_uri);
|
||||
|
||||
|
||||
// pre-process the scopes
|
||||
Set<SystemScope> scopes = scopeService.fromStrings(authRequest.getScope());
|
||||
|
||||
Set<SystemScope> sortedScopes = new LinkedHashSet<>(scopes.size());
|
||||
Set<SystemScope> systemScopes = scopeService.getAll();
|
||||
|
||||
// sort scopes for display based on the inherent order of system scopes
|
||||
for (SystemScope s : systemScopes) {
|
||||
if (scopes.contains(s)) {
|
||||
sortedScopes.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
// add in any scopes that aren't system scopes to the end of the list
|
||||
sortedScopes.addAll(Sets.difference(scopes, systemScopes));
|
||||
|
||||
model.put("scopes", sortedScopes);
|
||||
Set<SystemScope> sortedScopes = ControllerUtils.getSortedScopes(authRequest.getScope(), scopeService);
|
||||
model.put(SCOPES, sortedScopes);
|
||||
|
||||
// get the userinfo claims for each scope
|
||||
model.put(CLAIMS, getClaimsForScopes(p, sortedScopes));
|
||||
|
||||
// contacts
|
||||
if (client.getContacts() != null) {
|
||||
model.put(CONTACTS, Joiner.on(", ").join(client.getContacts()));
|
||||
}
|
||||
model.put(GRAS, true);
|
||||
|
||||
if (perunOidcConfig.getTheme().equalsIgnoreCase(DEFAULT)) {
|
||||
return APPROVE;
|
||||
}
|
||||
|
||||
PerunUserInfo perunUser = (PerunUserInfo) userInfoService.getByUsernameAndClientId(
|
||||
p.getName(), client.getClientId());
|
||||
ControllerUtils.setScopesAndClaims(scopeService, scopeClaimTranslationService, model, authRequest.getScope(),
|
||||
perunUser);
|
||||
ControllerUtils.setPageOptions(model, req, htmlClasses, perunOidcConfig);
|
||||
|
||||
model.put(PAGE, CONSENT);
|
||||
return THEMED_APPROVE;
|
||||
}
|
||||
|
||||
private String sendRedirect(AuthorizationRequest authRequest, Map<String, Object> model, ClientDetailsEntity client) {
|
||||
String url = redirectResolver.resolveRedirect(authRequest.getRedirectUri(), client);
|
||||
|
||||
try {
|
||||
URIBuilder uriBuilder = new URIBuilder(url);
|
||||
|
||||
uriBuilder.addParameter(ERROR, INTERACTION_REQUIRED);
|
||||
if (!Strings.isNullOrEmpty(authRequest.getState())) {
|
||||
uriBuilder.addParameter(STATE, authRequest.getState()); // copy the state parameter if one was given
|
||||
}
|
||||
|
||||
return REDIRECT + ":" + uriBuilder;
|
||||
|
||||
} catch (URISyntaxException e) {
|
||||
log.error("Can't build redirect URI for prompt=none, sending error instead", e);
|
||||
model.put(CODE, HttpStatus.FORBIDDEN);
|
||||
return HttpCodeView.VIEWNAME;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Map<String, String>> getClaimsForScopes(Principal p, Set<SystemScope> sortedScopes) {
|
||||
UserInfo user = userInfoService.getByUsername(p.getName());
|
||||
Map<String, Map<String, String>> claimsForScopes = new HashMap<>();
|
||||
|
||||
if (user != null) {
|
||||
JsonObject userJson = user.toJson();
|
||||
|
||||
|
@ -183,31 +234,6 @@ public class OAuthConfirmationController {
|
|||
}
|
||||
}
|
||||
|
||||
model.put("claims", claimsForScopes);
|
||||
|
||||
// contacts
|
||||
if (client.getContacts() != null) {
|
||||
String contacts = Joiner.on(", ").join(client.getContacts());
|
||||
model.put("contacts", contacts);
|
||||
}
|
||||
model.put("gras", true);
|
||||
|
||||
return "approve";
|
||||
return claimsForScopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the clientService
|
||||
*/
|
||||
public ClientDetailsEntityService getClientService() {
|
||||
return clientService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clientService the clientService to set
|
||||
*/
|
||||
public void setClientService(ClientDetailsEntityService clientService) {
|
||||
this.clientService = clientService;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package cz.muni.ics.oidc.saml;
|
||||
|
||||
import static cz.muni.ics.oidc.server.filters.PerunFilterConstants.AUTHORIZE_REQ_PATTERN;
|
||||
import static cz.muni.ics.oidc.server.filters.PerunFilterConstants.DEVICE_APPROVE_REQ_PATTERN;
|
||||
import static org.springframework.http.HttpHeaders.REFERER;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -16,25 +18,30 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
@Slf4j
|
||||
public class SamlInvalidateSessionFilter extends GenericFilterBean {
|
||||
|
||||
private final AntPathRequestMatcher matcher;
|
||||
private static final RequestMatcher AUTHORIZE_MATCHER = new AntPathRequestMatcher(AUTHORIZE_REQ_PATTERN);
|
||||
private static final RequestMatcher AUTHORIZE_ALL_MATCHER = new AntPathRequestMatcher(AUTHORIZE_REQ_PATTERN + "/**");
|
||||
private static final RequestMatcher DEVICE_CODE_MATCHER = new AntPathRequestMatcher(DEVICE_APPROVE_REQ_PATTERN);
|
||||
private static final RequestMatcher DEVICE_CODE_ALL_MATCHER = new AntPathRequestMatcher(DEVICE_APPROVE_REQ_PATTERN + "/**");
|
||||
private static final RequestMatcher MATCHER = new OrRequestMatcher(
|
||||
Arrays.asList(AUTHORIZE_MATCHER, AUTHORIZE_ALL_MATCHER, DEVICE_CODE_MATCHER, DEVICE_CODE_ALL_MATCHER));
|
||||
|
||||
private final SecurityContextLogoutHandler contextLogoutHandler;
|
||||
private final List<String> internalReferrers = new ArrayList<>();
|
||||
|
||||
public SamlInvalidateSessionFilter(String pattern,
|
||||
String idpEntityId,
|
||||
public SamlInvalidateSessionFilter(String idpEntityId,
|
||||
String oidcIssuer,
|
||||
String proxySpEntityId,
|
||||
SecurityContextLogoutHandler contextLogoutHandler,
|
||||
String[] internalReferrers)
|
||||
{
|
||||
this.matcher = new AntPathRequestMatcher(pattern);
|
||||
if (StringUtils.hasText(idpEntityId)) {
|
||||
this.internalReferrers.add(idpEntityId);
|
||||
}
|
||||
|
@ -60,9 +67,10 @@ public class SamlInvalidateSessionFilter extends GenericFilterBean {
|
|||
{
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
HttpServletResponse res = (HttpServletResponse) response;
|
||||
if (matcher.matches(req)) {
|
||||
if (MATCHER.matches(req)) {
|
||||
boolean isDeviceCodeFlow = DEVICE_CODE_MATCHER.matches(req) || DEVICE_CODE_ALL_MATCHER.matches(req);
|
||||
String referer = req.getHeader(REFERER);
|
||||
if (!isInternalReferer(referer)) {
|
||||
if (!isInternalReferer(referer, !isDeviceCodeFlow)) {
|
||||
log.debug("Got external referer, clear session to reauthenticate");
|
||||
contextLogoutHandler.logout(req, res, null);
|
||||
}
|
||||
|
@ -70,9 +78,9 @@ public class SamlInvalidateSessionFilter extends GenericFilterBean {
|
|||
chain.doFilter(req, res);
|
||||
}
|
||||
|
||||
private boolean isInternalReferer(String referer) {
|
||||
private boolean isInternalReferer(String referer, boolean emptyRefererAsInternal) {
|
||||
if (!StringUtils.hasText(referer)) { // no referer, consider as internal
|
||||
return true;
|
||||
return emptyRefererAsInternal;
|
||||
}
|
||||
for (String internal : internalReferrers) {
|
||||
if (referer.startsWith(internal)) {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package cz.muni.ics.oidc.server.filters;
|
||||
|
||||
import static cz.muni.ics.oidc.server.filters.PerunFilterConstants.AUTHORIZE_REQ_PATTERN;
|
||||
import static cz.muni.ics.oidc.server.filters.PerunFilterConstants.DEVICE_CHECK_CODE_REQ_PATTERN;
|
||||
|
||||
import cz.muni.ics.oauth2.model.ClientDetailsEntity;
|
||||
import cz.muni.ics.oauth2.service.ClientDetailsEntityService;
|
||||
import cz.muni.ics.oidc.BeanUtil;
|
||||
|
@ -8,6 +11,7 @@ import cz.muni.ics.oidc.models.PerunUser;
|
|||
import cz.muni.ics.oidc.saml.SamlProperties;
|
||||
import cz.muni.ics.oidc.server.adapters.PerunAdapter;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import javax.annotation.PostConstruct;
|
||||
|
@ -19,6 +23,9 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
|
@ -31,6 +38,13 @@ import org.springframework.web.filter.GenericFilterBean;
|
|||
@Slf4j
|
||||
public class CallPerunFiltersFilter extends GenericFilterBean {
|
||||
|
||||
private static final RequestMatcher AUTHORIZE_MATCHER = new AntPathRequestMatcher(AUTHORIZE_REQ_PATTERN);
|
||||
private static final RequestMatcher AUTHORIZE_ALL_MATCHER = new AntPathRequestMatcher(AUTHORIZE_REQ_PATTERN + "/**");
|
||||
private static final RequestMatcher DEVICE_CODE_MATCHER = new AntPathRequestMatcher(DEVICE_CHECK_CODE_REQ_PATTERN);
|
||||
private static final RequestMatcher DEVICE_CODE_ALL_MATCHER = new AntPathRequestMatcher(DEVICE_CHECK_CODE_REQ_PATTERN + "/**");
|
||||
private static final RequestMatcher MATCHER = new OrRequestMatcher(
|
||||
Arrays.asList(AUTHORIZE_MATCHER, AUTHORIZE_ALL_MATCHER, DEVICE_CODE_MATCHER, DEVICE_CODE_ALL_MATCHER));
|
||||
|
||||
@Autowired
|
||||
private Properties coreProperties;
|
||||
|
||||
|
@ -60,25 +74,30 @@ public class CallPerunFiltersFilter extends GenericFilterBean {
|
|||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
List<PerunRequestFilter> filters = perunFiltersContext.getFilters();
|
||||
if (filters != null && !filters.isEmpty()) {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
ClientDetailsEntity client = FiltersUtils.extractClientFromRequest(request, authRequestFactory,
|
||||
clientDetailsEntityService);
|
||||
Facility facility = null;
|
||||
if (client != null && StringUtils.hasText(client.getClientId())) {
|
||||
try {
|
||||
facility = perunAdapter.getFacilityByClientId(client.getClientId());
|
||||
} catch (Exception e) {
|
||||
log.warn("{} - could not fetch facility for client_id '{}'",
|
||||
CallPerunFiltersFilter.class.getSimpleName(), client.getClientId(), e);
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
if (!MATCHER.matches(request)) {
|
||||
log.debug("Custom filters have been skipped, did not match '/authorize' nor '/device/code' request");
|
||||
} else {
|
||||
List<PerunRequestFilter> filters = perunFiltersContext.getFilters();
|
||||
if (filters != null && !filters.isEmpty()) {
|
||||
ClientDetailsEntity client = FiltersUtils.extractClientFromRequest(request, authRequestFactory,
|
||||
clientDetailsEntityService);
|
||||
Facility facility = null;
|
||||
if (client != null && StringUtils.hasText(client.getClientId())) {
|
||||
try {
|
||||
facility = perunAdapter.getFacilityByClientId(client.getClientId());
|
||||
} catch (Exception e) {
|
||||
log.warn("{} - could not fetch facility for client_id '{}'",
|
||||
CallPerunFiltersFilter.class.getSimpleName(), client.getClientId(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
PerunUser user = FiltersUtils.getPerunUser(request, perunAdapter, samlProperties.getUserIdentifierAttribute());
|
||||
FilterParams params = new FilterParams(client, facility, user);
|
||||
for (PerunRequestFilter filter : filters) {
|
||||
if (!filter.doFilter(servletRequest, servletResponse, params)) {
|
||||
return;
|
||||
PerunUser user = FiltersUtils.getPerunUser(request, perunAdapter,
|
||||
samlProperties.getUserIdentifierAttribute());
|
||||
FilterParams params = new FilterParams(client, facility, user);
|
||||
for (PerunRequestFilter filter : filters) {
|
||||
if (!filter.doFilter(servletRequest, servletResponse, params)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package cz.muni.ics.oidc.server.filters;
|
||||
|
||||
import static cz.muni.ics.oauth2.web.DeviceEndpoint.DEVICE_CODE_SESSION_ATTRIBUTE;
|
||||
import static cz.muni.ics.oidc.server.filters.PerunFilterConstants.PARAM_FORCE_AUTHN;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import cz.muni.ics.oauth2.model.ClientDetailsEntity;
|
||||
import cz.muni.ics.oauth2.model.DeviceCode;
|
||||
import cz.muni.ics.oauth2.service.ClientDetailsEntityService;
|
||||
import cz.muni.ics.oidc.models.Facility;
|
||||
import cz.muni.ics.oidc.models.PerunAttributeValue;
|
||||
|
@ -20,13 +21,9 @@ import java.util.stream.Collectors;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.oauth2.provider.AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
|
||||
import org.springframework.security.providers.ExpiringUsernameAuthenticationToken;
|
||||
import org.springframework.security.saml.SAMLCredential;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
@ -37,8 +34,6 @@ import org.springframework.util.StringUtils;
|
|||
@Slf4j
|
||||
public class FiltersUtils {
|
||||
|
||||
private static final RequestMatcher requestMatcher = new AntPathRequestMatcher(PerunFilterConstants.AUTHORIZE_REQ_PATTERN);
|
||||
|
||||
/**
|
||||
* Create map of request params in format key = name, value = paramValue.
|
||||
*
|
||||
|
@ -69,21 +64,29 @@ public class FiltersUtils {
|
|||
OAuth2RequestFactory authRequestFactory,
|
||||
ClientDetailsEntityService clientService)
|
||||
{
|
||||
if (!requestMatcher.matches(request) || request.getParameter("response_type") == null) {
|
||||
if (request.getParameter("response_type") == null
|
||||
&& request.getSession() == null
|
||||
&& request.getSession().getAttribute(DEVICE_CODE_SESSION_ATTRIBUTE) == null
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AuthorizationRequest authRequest = authRequestFactory.createAuthorizationRequest(
|
||||
FiltersUtils.createRequestMap(request.getParameterMap()));
|
||||
String clientId;
|
||||
if (request.getSession() != null && request.getSession().getAttribute(DEVICE_CODE_SESSION_ATTRIBUTE) != null) {
|
||||
clientId = ((DeviceCode) request.getSession().getAttribute(DEVICE_CODE_SESSION_ATTRIBUTE)).getClientId();
|
||||
} else {
|
||||
clientId = authRequestFactory.createAuthorizationRequest(
|
||||
FiltersUtils.createRequestMap(request.getParameterMap())).getClientId();
|
||||
}
|
||||
|
||||
ClientDetailsEntity client;
|
||||
if (Strings.isNullOrEmpty(authRequest.getClientId())) {
|
||||
if (!StringUtils.hasText(clientId)) {
|
||||
log.debug("cannot extract client - ClientID is null or empty");
|
||||
return null;
|
||||
}
|
||||
|
||||
client = clientService.loadClientByClientId(authRequest.getClientId());
|
||||
if (Strings.isNullOrEmpty(client.getClientName())) {
|
||||
client = clientService.loadClientByClientId(clientId);
|
||||
if (!StringUtils.hasText(client.getClientName())) {
|
||||
log.warn("cannot extract clientName for the clientID '{}'", client.getClientId());
|
||||
return null;
|
||||
}
|
||||
|
@ -207,11 +210,11 @@ public class FiltersUtils {
|
|||
|
||||
/**
|
||||
* Redirect user to the unapproved page.
|
||||
* @param request original request object
|
||||
* @param base Base URL
|
||||
* @param response response object
|
||||
* @param clientId identifier of the service
|
||||
*/
|
||||
public static void redirectUnapproved(HttpServletRequest request, HttpServletResponse response, String clientId, String redirectMapping)
|
||||
public static void redirectUnapproved(String base, HttpServletResponse response, String clientId, String redirectMapping)
|
||||
{
|
||||
// cannot register, redirect to unapproved
|
||||
Map<String, String> params = new HashMap<>();
|
||||
|
@ -219,8 +222,7 @@ public class FiltersUtils {
|
|||
params.put("client_id", clientId);
|
||||
}
|
||||
|
||||
String redirectUrl = ControllerUtils.createRedirectUrl(request, PerunFilterConstants.AUTHORIZE_REQ_PATTERN,
|
||||
redirectMapping, params);
|
||||
String redirectUrl = ControllerUtils.createRedirectUrl(base, redirectMapping, params);
|
||||
response.reset();
|
||||
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
|
||||
response.setHeader("Location", redirectUrl);
|
||||
|
@ -228,7 +230,7 @@ public class FiltersUtils {
|
|||
|
||||
/**
|
||||
* Redirect user to the correct page when cannot access the service based on membership.
|
||||
* @param request Request object
|
||||
* @param base base URL
|
||||
* @param response Response object
|
||||
* @param facility Facility representing the client
|
||||
* @param user User accessing the service
|
||||
|
@ -237,7 +239,7 @@ public class FiltersUtils {
|
|||
* @param facilityAttributes Actual facility attributes
|
||||
* @param perunAdapter Adapter to call Perun
|
||||
*/
|
||||
public static void redirectUserCannotAccess(HttpServletRequest request,
|
||||
public static void redirectUserCannotAccess(String base,
|
||||
HttpServletResponse response,
|
||||
Facility facility,
|
||||
PerunUser user,
|
||||
|
@ -263,7 +265,7 @@ public class FiltersUtils {
|
|||
|
||||
if (facilityAttributes.get(facilityAttrsConfig.getDynamicRegistrationAttr()).valueAsBoolean()) {
|
||||
// redirect to registration form
|
||||
FiltersUtils.redirectToRegistrationForm(request, response, clientIdentifier, facility, user);
|
||||
FiltersUtils.redirectToRegistrationForm(base, response, clientIdentifier, facility, user);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -271,7 +273,7 @@ public class FiltersUtils {
|
|||
|
||||
// cannot register, redirect to unapproved
|
||||
log.debug("user cannot register to obtain access, redirecting user '{}' to unapproved page", user);
|
||||
FiltersUtils.redirectUnapproved(request, response, clientIdentifier, redirectUrl);
|
||||
FiltersUtils.redirectUnapproved(base, response, clientIdentifier, redirectUrl);
|
||||
}
|
||||
|
||||
public static String fillStringMandatoryProperty(String propertyName,
|
||||
|
@ -286,13 +288,13 @@ public class FiltersUtils {
|
|||
return filled;
|
||||
}
|
||||
|
||||
private static void redirectToRegistrationForm(HttpServletRequest request, HttpServletResponse response,
|
||||
private static void redirectToRegistrationForm(String base, HttpServletResponse response,
|
||||
String clientIdentifier, Facility facility, PerunUser user) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("client_id", clientIdentifier);
|
||||
params.put("facility_id", facility.getId().toString());
|
||||
params.put("user_id", String.valueOf(user.getId()));
|
||||
String redirectUrl = ControllerUtils.createRedirectUrl(request, PerunFilterConstants.AUTHORIZE_REQ_PATTERN,
|
||||
String redirectUrl = ControllerUtils.createRedirectUrl(base,
|
||||
PerunUnapprovedRegistrationController.REGISTRATION_CONTINUE_MAPPING, params);
|
||||
log.debug("redirecting user '{}' to the registration form URL: {}", user, redirectUrl);
|
||||
response.reset();
|
||||
|
|
|
@ -12,9 +12,8 @@ import java.util.Map;
|
|||
public class PerunFilterConstants {
|
||||
|
||||
public static final String AUTHORIZE_REQ_PATTERN = "/authorize";
|
||||
public static final String SHIB_IDENTITY_PROVIDER = "Shib-Identity-Provider";
|
||||
public static final String SHIB_AUTHN_CONTEXT_CLASS = "Shib-AuthnContext-Class";
|
||||
public static final String SHIB_AUTHN_CONTEXT_METHOD = "Shib-Authentication-Method";
|
||||
public static final String DEVICE_APPROVE_REQ_PATTERN = "/device/code";
|
||||
public static final String DEVICE_CHECK_CODE_REQ_PATTERN = "/device/checkcode";
|
||||
|
||||
public static final String PARAM_CLIENT_ID = "client_id";
|
||||
public static final String PARAM_SCOPE = "scope";
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package cz.muni.ics.oidc.server.filters;
|
||||
|
||||
import static cz.muni.ics.oidc.server.filters.PerunFilterConstants.AUTHORIZE_REQ_PATTERN;
|
||||
import static cz.muni.ics.oidc.server.filters.PerunFilterConstants.DEVICE_APPROVE_REQ_PATTERN;
|
||||
import static cz.muni.ics.oidc.server.filters.PerunFilterConstants.DEVICE_CHECK_CODE_REQ_PATTERN;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
@ -11,6 +13,7 @@ import javax.servlet.ServletResponse;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
/**
|
||||
|
@ -42,8 +45,6 @@ public abstract class PerunRequestFilter {
|
|||
private static final String CLIENT_IDS = "clientIds";
|
||||
private static final String SUBS = "subs";
|
||||
|
||||
private static final RequestMatcher requestMatcher = new AntPathRequestMatcher(AUTHORIZE_REQ_PATTERN);
|
||||
|
||||
private final String filterName;
|
||||
private Set<String> clientIds = new HashSet<>();
|
||||
private Set<String> subs = new HashSet<>();
|
||||
|
@ -77,11 +78,6 @@ public abstract class PerunRequestFilter {
|
|||
|
||||
public boolean doFilter(ServletRequest req, ServletResponse res, FilterParams params) throws IOException {
|
||||
HttpServletRequest request = (HttpServletRequest) req;
|
||||
// skip everything that's not an authorize URL
|
||||
if (!requestMatcher.matches(request)) {
|
||||
log.debug("{} - filter has been skipped, did not match '/authorize' the request", filterName);
|
||||
return true;
|
||||
}
|
||||
if (!skip(request)) {
|
||||
log.trace("{} - executing filter", filterName);
|
||||
return this.process(req, res, params);
|
||||
|
|
|
@ -6,6 +6,7 @@ import cz.muni.ics.oidc.models.PerunAttributeValue;
|
|||
import cz.muni.ics.oidc.models.PerunUser;
|
||||
import cz.muni.ics.oidc.server.adapters.PerunAdapter;
|
||||
import cz.muni.ics.oidc.server.configurations.FacilityAttrsConfig;
|
||||
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
||||
import cz.muni.ics.oidc.server.filters.FilterParams;
|
||||
import cz.muni.ics.oidc.server.filters.FiltersUtils;
|
||||
import cz.muni.ics.oidc.server.filters.PerunRequestFilter;
|
||||
|
@ -35,6 +36,7 @@ public class PerunAuthorizationFilter extends PerunRequestFilter {
|
|||
private final PerunAdapter perunAdapter;
|
||||
private final FacilityAttrsConfig facilityAttrsConfig;
|
||||
private final String filterName;
|
||||
private final PerunOidcConfig config;
|
||||
|
||||
public PerunAuthorizationFilter(PerunRequestFilterParams params) {
|
||||
super(params);
|
||||
|
@ -42,6 +44,7 @@ public class PerunAuthorizationFilter extends PerunRequestFilter {
|
|||
this.perunAdapter = beanUtil.getBean(PerunAdapter.class);
|
||||
this.facilityAttrsConfig = beanUtil.getBean(FacilityAttrsConfig.class);
|
||||
this.filterName = params.getFilterName();
|
||||
this.config = beanUtil.getBean(PerunOidcConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,7 +84,7 @@ public class PerunAuthorizationFilter extends PerunRequestFilter {
|
|||
log.info("{} - user allowed to access the service", filterName);
|
||||
return true;
|
||||
} else {
|
||||
FiltersUtils.redirectUserCannotAccess(request, response, facility, user, clientIdentifier,
|
||||
FiltersUtils.redirectUserCannotAccess(config.getConfigBean().getIssuer(), response, facility, user, clientIdentifier,
|
||||
facilityAttrsConfig, facilityAttributes, perunAdapter,
|
||||
PerunUnapprovedController.UNAPPROVED_AUTHORIZATION);
|
||||
return false;
|
||||
|
|
|
@ -11,6 +11,7 @@ import cz.muni.ics.oidc.BeanUtil;
|
|||
import cz.muni.ics.oidc.models.PerunAttributeValue;
|
||||
import cz.muni.ics.oidc.models.PerunUser;
|
||||
import cz.muni.ics.oidc.server.adapters.PerunAdapter;
|
||||
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
||||
import cz.muni.ics.oidc.server.filters.FilterParams;
|
||||
import cz.muni.ics.oidc.server.filters.FiltersUtils;
|
||||
import cz.muni.ics.oidc.server.filters.PerunFilterConstants;
|
||||
|
@ -58,12 +59,14 @@ public class PerunIsCesnetEligibleFilter extends PerunRequestFilter {
|
|||
private final int validityPeriod;
|
||||
/* END OF CONFIGURATION PROPERTIES */
|
||||
|
||||
private final PerunOidcConfig config;
|
||||
private final PerunAdapter perunAdapter;
|
||||
private final String filterName;
|
||||
|
||||
public PerunIsCesnetEligibleFilter(PerunRequestFilterParams params) {
|
||||
super(params);
|
||||
BeanUtil beanUtil = params.getBeanUtil();
|
||||
this.config = beanUtil.getBean(PerunOidcConfig.class);
|
||||
this.perunAdapter = beanUtil.getBean(PerunAdapter.class);
|
||||
this.isCesnetEligibleAttrName = params.getProperty(IS_CESNET_ELIGIBLE_ATTR_NAME);
|
||||
this.triggerScope = params.getProperty(IS_CESNET_ELIGIBLE_SCOPE);
|
||||
|
@ -132,7 +135,7 @@ public class PerunIsCesnetEligibleFilter extends PerunRequestFilter {
|
|||
params.put(PARAM_TARGET, targetURL);
|
||||
params.put(PARAM_REASON, reason);
|
||||
|
||||
String redirectUrl = ControllerUtils.createRedirectUrl(req, PerunFilterConstants.AUTHORIZE_REQ_PATTERN,
|
||||
String redirectUrl = ControllerUtils.createRedirectUrl(config.getConfigBean().getIssuer(),
|
||||
PerunUnapprovedController.UNAPPROVED_IS_CESNET_ELIGIBLE_MAPPING, params);
|
||||
log.debug("{} - redirecting user to unapproved: URL '{}'", filterName, redirectUrl);
|
||||
res.reset();
|
||||
|
|
|
@ -7,6 +7,7 @@ import cz.muni.ics.oidc.BeanUtil;
|
|||
import cz.muni.ics.oidc.models.Facility;
|
||||
import cz.muni.ics.oidc.models.PerunAttributeValue;
|
||||
import cz.muni.ics.oidc.server.adapters.PerunAdapter;
|
||||
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
||||
import cz.muni.ics.oidc.server.filters.FilterParams;
|
||||
import cz.muni.ics.oidc.server.filters.FiltersUtils;
|
||||
import cz.muni.ics.oidc.server.filters.PerunFilterConstants;
|
||||
|
@ -43,6 +44,7 @@ public class PerunIsTestSpFilter extends PerunRequestFilter {
|
|||
private final String isTestSpAttrName;
|
||||
private final PerunAdapter perunAdapter;
|
||||
private final String filterName;
|
||||
private final PerunOidcConfig config;
|
||||
|
||||
public PerunIsTestSpFilter(PerunRequestFilterParams params) {
|
||||
super(params);
|
||||
|
@ -50,6 +52,7 @@ public class PerunIsTestSpFilter extends PerunRequestFilter {
|
|||
this.perunAdapter = beanUtil.getBean(PerunAdapter.class);
|
||||
this.isTestSpAttrName = params.getProperty(IS_TEST_SP_ATTR_NAME);
|
||||
this.filterName = params.getFilterName();
|
||||
this.config = beanUtil.getBean(PerunOidcConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -95,7 +98,7 @@ public class PerunIsTestSpFilter extends PerunRequestFilter {
|
|||
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put(PARAM_TARGET, targetURL);
|
||||
String redirectUrl = ControllerUtils.createRedirectUrl(req, PerunFilterConstants.AUTHORIZE_REQ_PATTERN,
|
||||
String redirectUrl = ControllerUtils.createRedirectUrl(config.getConfigBean().getIssuer(),
|
||||
IsTestSpController.MAPPING, params);
|
||||
log.debug("{} - redirecting user to testSP warning page: {}", filterName, redirectUrl);
|
||||
res.reset();
|
||||
|
|
|
@ -6,6 +6,7 @@ import cz.muni.ics.oidc.models.PerunAttributeValue;
|
|||
import cz.muni.ics.oidc.models.PerunUser;
|
||||
import cz.muni.ics.oidc.server.adapters.PerunAdapter;
|
||||
import cz.muni.ics.oidc.server.configurations.FacilityAttrsConfig;
|
||||
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
||||
import cz.muni.ics.oidc.server.filters.FilterParams;
|
||||
import cz.muni.ics.oidc.server.filters.FiltersUtils;
|
||||
import cz.muni.ics.oidc.server.filters.PerunRequestFilter;
|
||||
|
@ -66,6 +67,7 @@ public class ValidUserFilter extends PerunRequestFilter {
|
|||
private final PerunAdapter perunAdapter;
|
||||
private final FacilityAttrsConfig facilityAttrsConfig;
|
||||
private final String filterName;
|
||||
private final PerunOidcConfig config;
|
||||
|
||||
public ValidUserFilter(PerunRequestFilterParams params) {
|
||||
super(params);
|
||||
|
@ -80,6 +82,7 @@ public class ValidUserFilter extends PerunRequestFilter {
|
|||
this.prodEnvGroups = this.getIdsFromParam(params, PROD_ENV_GROUPS);
|
||||
this.prodEnvVos = this.getIdsFromParam(params, PROD_ENV_VOS);
|
||||
this.filterName = params.getFilterName();
|
||||
this.config = beanUtil.getBean(PerunOidcConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -103,7 +106,7 @@ public class ValidUserFilter extends PerunRequestFilter {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!checkMemberValidInGroupsAndVos(user, facility, request, response, params, allEnvVos, allEnvGroups,
|
||||
if (!checkMemberValidInGroupsAndVos(user, facility, response, params, allEnvVos, allEnvGroups,
|
||||
PerunUnapprovedController.UNAPPROVED_NOT_IN_MANDATORY_VOS_GROUPS)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -118,7 +121,7 @@ public class ValidUserFilter extends PerunRequestFilter {
|
|||
additionalVos.addAll(testEnvVos);
|
||||
additionalGroups.addAll(testEnvGroups);
|
||||
|
||||
if (!checkMemberValidInGroupsAndVos(user, facility, request, response, params, additionalVos,
|
||||
if (!checkMemberValidInGroupsAndVos(user, facility, response, params, additionalVos,
|
||||
additionalGroups, PerunUnapprovedController.UNAPPROVED_NOT_IN_TEST_VOS_GROUPS)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -126,7 +129,7 @@ public class ValidUserFilter extends PerunRequestFilter {
|
|||
additionalVos.addAll(prodEnvVos);
|
||||
additionalGroups.addAll(prodEnvGroups);
|
||||
|
||||
if (!checkMemberValidInGroupsAndVos(user, facility, request, response, params, additionalVos,
|
||||
if (!checkMemberValidInGroupsAndVos(user, facility, response, params, additionalVos,
|
||||
additionalGroups, PerunUnapprovedController.UNAPPROVED_NOT_IN_PROD_VOS_GROUPS)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -153,7 +156,6 @@ public class ValidUserFilter extends PerunRequestFilter {
|
|||
private boolean checkMemberValidInGroupsAndVos(
|
||||
PerunUser user,
|
||||
Facility facility,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
FilterParams params,
|
||||
Set<Long> vos,
|
||||
|
@ -168,8 +170,8 @@ public class ValidUserFilter extends PerunRequestFilter {
|
|||
Map<String, PerunAttributeValue> facilityAttributes = perunAdapter.getFacilityAttributeValues(
|
||||
facility, facilityAttrsConfig.getMembershipAttrNames());
|
||||
|
||||
FiltersUtils.redirectUserCannotAccess(request, response, facility, user, params.getClientIdentifier(),
|
||||
facilityAttrsConfig, facilityAttributes, perunAdapter, redirectUrl);
|
||||
FiltersUtils.redirectUserCannotAccess(config.getConfigBean().getIssuer(), response, facility, user,
|
||||
params.getClientIdentifier(), facilityAttrsConfig, facilityAttributes, perunAdapter, redirectUrl);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,189 +0,0 @@
|
|||
package cz.muni.ics.oidc.web.controllers;
|
||||
|
||||
import cz.muni.ics.oauth2.model.ClientDetailsEntity;
|
||||
import cz.muni.ics.oauth2.model.DeviceCode;
|
||||
import cz.muni.ics.oauth2.service.SystemScopeService;
|
||||
import cz.muni.ics.oauth2.web.DeviceEndpoint;
|
||||
import cz.muni.ics.oidc.server.PerunScopeClaimTranslationService;
|
||||
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
||||
import cz.muni.ics.oidc.server.filters.PerunFilterConstants;
|
||||
import cz.muni.ics.oidc.server.userInfo.PerunUserInfo;
|
||||
import cz.muni.ics.oidc.web.WebHtmlClasses;
|
||||
import cz.muni.ics.openid.connect.service.UserInfoService;
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.provider.AuthorizationRequest;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Controller
|
||||
public class ApproveDeviceController {
|
||||
|
||||
public static final String DEVICE = "device";
|
||||
public static final String APPROVE_DEVICE = "approveDevice";
|
||||
public static final String DEVICE_APPROVED = "deviceApproved";
|
||||
public static final String REQUEST_USER_CODE = "requestUserCode";
|
||||
public static final String USER_CODE = "user_code";
|
||||
public static final String USER_OAUTH_APPROVAL = "user_oauth_approval";
|
||||
public static final String URL = "devicecode";
|
||||
public static final String VERIFICATION_URI = "verification_uri";
|
||||
public static final String VERIFICATION_URI_COMPLETE = "verification_uri_complete";
|
||||
public static final String ACR_VALUES = "acr_values";
|
||||
public static final String ENTITY = "entity";
|
||||
public static final String CLIENT_ID = "client_id";
|
||||
public static final String SCOPE = "scope";
|
||||
public static final String ACR = "acr";
|
||||
|
||||
private final SystemScopeService scopeService;
|
||||
private final DeviceEndpoint deviceEndpoint;
|
||||
private final PerunOidcConfig perunOidcConfig;
|
||||
private final WebHtmlClasses htmlClasses;
|
||||
private final PerunScopeClaimTranslationService scopeClaimTranslationService;
|
||||
private final UserInfoService userInfoService;
|
||||
|
||||
@Autowired
|
||||
public ApproveDeviceController(SystemScopeService scopeService,
|
||||
DeviceEndpoint deviceEndpoint,
|
||||
PerunOidcConfig perunOidcConfig,
|
||||
WebHtmlClasses htmlClasses,
|
||||
PerunScopeClaimTranslationService scopeClaimTranslationService,
|
||||
UserInfoService userInfoService)
|
||||
{
|
||||
this.scopeService = scopeService;
|
||||
this.deviceEndpoint = deviceEndpoint;
|
||||
this.perunOidcConfig = perunOidcConfig;
|
||||
this.htmlClasses = htmlClasses;
|
||||
this.scopeClaimTranslationService = scopeClaimTranslationService;
|
||||
this.userInfoService = userInfoService;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
value = {"/" + URL},
|
||||
method = RequestMethod.POST,
|
||||
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
|
||||
produces = {MediaType.APPLICATION_JSON_VALUE},
|
||||
params = {CLIENT_ID, ACR_VALUES}
|
||||
)
|
||||
public String requestDeviceCodeMFA(@RequestParam(CLIENT_ID) String clientId, @RequestParam(name = SCOPE, required = false) String scope,
|
||||
@RequestParam(name = ACR_VALUES) String acrValues, Map<String, String> parameters, ModelMap model)
|
||||
{
|
||||
String result = deviceEndpoint.requestDeviceCode(clientId, scope, parameters, model);
|
||||
|
||||
Map<String, Object> response = (Map<String, Object>) model.get(ENTITY);
|
||||
response.replace(VERIFICATION_URI, response.get(VERIFICATION_URI) + "?" + ACR_VALUES + "=" + acrValues);
|
||||
response.replace(VERIFICATION_URI_COMPLETE, response.get(VERIFICATION_URI_COMPLETE) + "&" + ACR_VALUES + "=" + acrValues);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@GetMapping(value = "/" + DEVICE,
|
||||
consumes = {"text/html", "application/xhtml+xml","application/xml;q=0.9","image/webp","*/*;q=0.8"})
|
||||
public String requestUserCode(@RequestParam(value = USER_CODE, required = false) String userCode,
|
||||
@ModelAttribute("authorizationRequest") AuthorizationRequest authRequest,
|
||||
Principal p,
|
||||
HttpServletRequest req,
|
||||
ModelMap model,
|
||||
HttpSession session)
|
||||
{
|
||||
String result = deviceEndpoint.requestUserCode(userCode, model, session);
|
||||
if (result.equals(REQUEST_USER_CODE) && !perunOidcConfig.getTheme().equalsIgnoreCase("default")) {
|
||||
ControllerUtils.setPageOptions(model, req, htmlClasses, perunOidcConfig);
|
||||
model.put("page", REQUEST_USER_CODE);
|
||||
String shibAuthnContextClass = "";
|
||||
if (StringUtils.hasText(req.getParameter(ACR_VALUES))) {
|
||||
shibAuthnContextClass = (String) req.getAttribute(PerunFilterConstants.SHIB_AUTHN_CONTEXT_CLASS);
|
||||
if (!StringUtils.hasText(shibAuthnContextClass)) {
|
||||
shibAuthnContextClass = (String) req.getAttribute(PerunFilterConstants.SHIB_AUTHN_CONTEXT_METHOD);
|
||||
}
|
||||
if (!StringUtils.hasText(shibAuthnContextClass)) {
|
||||
shibAuthnContextClass = "";
|
||||
}
|
||||
}
|
||||
model.put(ACR, shibAuthnContextClass);
|
||||
return "themedRequestUserCode";
|
||||
} else if (result.equals(APPROVE_DEVICE) && !perunOidcConfig.getTheme().equalsIgnoreCase("default")) {
|
||||
return themedApproveDevice(model, p, req);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@PostMapping(value = "/" + DEVICE + "/verify",
|
||||
consumes = {"text/html", "application/xhtml+xml","application/xml;q=0.9","image/webp","*/*;q=0.8"})
|
||||
public String readUserCode(@RequestParam(USER_CODE) String userCode,
|
||||
@ModelAttribute("authorizationRequest") AuthorizationRequest authRequest,
|
||||
Principal p,
|
||||
HttpServletRequest req,
|
||||
ModelMap model,
|
||||
HttpSession session)
|
||||
{
|
||||
String result = deviceEndpoint.readUserCode(userCode, model, session);
|
||||
if (result.equals(APPROVE_DEVICE) && !perunOidcConfig.getTheme().equalsIgnoreCase("default")) {
|
||||
return themedApproveDevice(model, p, req);
|
||||
} else if (result.equals(REQUEST_USER_CODE) && !perunOidcConfig.getTheme().equalsIgnoreCase("default")) {
|
||||
ControllerUtils.setPageOptions(model, req, htmlClasses, perunOidcConfig);
|
||||
model.put("page", REQUEST_USER_CODE);
|
||||
return "themedRequestUserCode";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_USER')")
|
||||
@PostMapping(value = "/" + DEVICE + "/approve", params = {USER_CODE, USER_OAUTH_APPROVAL})
|
||||
public String approveDevice(@RequestParam(USER_CODE) String userCode,
|
||||
@RequestParam(USER_OAUTH_APPROVAL) Boolean approve,
|
||||
@ModelAttribute(USER_OAUTH_APPROVAL) AuthorizationRequest authRequest,
|
||||
Principal p,
|
||||
HttpServletRequest req,
|
||||
ModelMap model,
|
||||
Authentication auth,
|
||||
HttpSession session)
|
||||
{
|
||||
String result = deviceEndpoint.approveDevice(userCode, approve, model, auth, session);
|
||||
if (result.equals(DEVICE_APPROVED) && !perunOidcConfig.getTheme().equalsIgnoreCase("default")) {
|
||||
model.remove("scopes");
|
||||
|
||||
DeviceCode dc = (DeviceCode)session.getAttribute("deviceCode");
|
||||
ClientDetailsEntity client = (ClientDetailsEntity) model.get("client");
|
||||
PerunUserInfo user = (PerunUserInfo) userInfoService.getByUsernameAndClientId(
|
||||
p.getName(), client.getClientId());
|
||||
|
||||
ControllerUtils.setScopesAndClaims(scopeService, scopeClaimTranslationService, model, dc.getScope(), user);
|
||||
ControllerUtils.setPageOptions(model, req, htmlClasses, perunOidcConfig);
|
||||
|
||||
model.put("page", DEVICE_APPROVED);
|
||||
return "themedDeviceApproved";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String themedApproveDevice(ModelMap model, Principal p, HttpServletRequest req) {
|
||||
model.remove("scopes");
|
||||
DeviceCode dc = (DeviceCode) model.get("dc");
|
||||
ClientDetailsEntity client = (ClientDetailsEntity) model.get("client");
|
||||
PerunUserInfo user = (PerunUserInfo) userInfoService.getByUsernameAndClientId(
|
||||
p.getName(), client.getClientId());
|
||||
ControllerUtils.setScopesAndClaims(scopeService, scopeClaimTranslationService, model, dc.getScope(), user);
|
||||
ControllerUtils.setPageOptions(model, req, htmlClasses, perunOidcConfig);
|
||||
|
||||
model.put("page", APPROVE_DEVICE);
|
||||
return "themedApproveDevice";
|
||||
}
|
||||
|
||||
}
|
|
@ -7,7 +7,6 @@ import com.google.gson.JsonElement;
|
|||
import com.google.gson.JsonObject;
|
||||
import cz.muni.ics.oauth2.model.SystemScope;
|
||||
import cz.muni.ics.oauth2.service.SystemScopeService;
|
||||
import cz.muni.ics.oidc.server.PerunScopeClaimTranslationService;
|
||||
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
||||
import cz.muni.ics.oidc.web.WebHtmlClasses;
|
||||
import cz.muni.ics.openid.connect.model.UserInfo;
|
||||
|
@ -25,6 +24,8 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import cz.muni.ics.openid.connect.service.ScopeClaimTranslationService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
|
@ -83,23 +84,24 @@ public class ControllerUtils {
|
|||
/**
|
||||
* Create redirect URL.
|
||||
*
|
||||
* @param request Request object
|
||||
* @param removedPart Part of URL to be removed
|
||||
* @param pathPart What to include as Path
|
||||
* @param params Map object of parameters
|
||||
* @param base Base of URL, might include some path already
|
||||
* @param path What to include as Path
|
||||
* @param params Map object of parameters
|
||||
* @return Modified redirect URL
|
||||
*/
|
||||
public static String createRedirectUrl(HttpServletRequest request, String removedPart,
|
||||
String pathPart, Map<String, String> params) {
|
||||
String baseUrl = request.getRequestURL().toString();
|
||||
int endIndex = baseUrl.indexOf(removedPart);
|
||||
if (endIndex > 1) {
|
||||
baseUrl = baseUrl.substring(0, endIndex);
|
||||
}
|
||||
|
||||
public static String createRedirectUrl(String base,
|
||||
String path,
|
||||
Map<String, String> params)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(baseUrl);
|
||||
builder.append(pathPart);
|
||||
if (!base.endsWith("/")) {
|
||||
base += '/';
|
||||
}
|
||||
builder.append(base);
|
||||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
builder.append(path);
|
||||
if (!params.isEmpty()) {
|
||||
builder.append('?');
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
|
@ -148,7 +150,7 @@ public class ControllerUtils {
|
|||
* @param user userInfo object
|
||||
*/
|
||||
public static void setScopesAndClaims(SystemScopeService scopeService,
|
||||
PerunScopeClaimTranslationService translationService,
|
||||
ScopeClaimTranslationService translationService,
|
||||
Map<String, Object> model,
|
||||
Set<String> scope,
|
||||
UserInfo user) {
|
||||
|
@ -291,4 +293,20 @@ public class ControllerUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static Set<SystemScope> getSortedScopes(Set<String> requestedScopes, SystemScopeService scopeService) {
|
||||
Set<SystemScope> scopes = scopeService.fromStrings(requestedScopes);
|
||||
|
||||
Set<SystemScope> sortedScopes = new LinkedHashSet<>(scopes.size());
|
||||
Set<SystemScope> systemScopes = scopeService.getAll();
|
||||
|
||||
for (SystemScope s : systemScopes) {
|
||||
if (scopes.contains(s)) {
|
||||
sortedScopes.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
sortedScopes.addAll(Sets.difference(scopes, systemScopes));
|
||||
return sortedScopes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
package cz.muni.ics.oidc.web.controllers;
|
||||
|
||||
|
||||
import cz.muni.ics.oauth2.model.ClientDetailsEntity;
|
||||
import cz.muni.ics.oauth2.service.SystemScopeService;
|
||||
import cz.muni.ics.oauth2.web.OAuthConfirmationController;
|
||||
import cz.muni.ics.oidc.server.PerunScopeClaimTranslationService;
|
||||
import cz.muni.ics.oidc.server.configurations.PerunOidcConfig;
|
||||
import cz.muni.ics.oidc.server.userInfo.PerunUserInfo;
|
||||
import cz.muni.ics.oidc.web.WebHtmlClasses;
|
||||
import cz.muni.ics.openid.connect.service.UserInfoService;
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.oauth2.provider.AuthorizationRequest;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
|
||||
/**
|
||||
* Controller of the pages where user accepts that information
|
||||
* about him will be sent to the client.
|
||||
*
|
||||
* @author Dominik František Bučík <bucik@ics.muni.cz>
|
||||
* @author Peter Jancus <jancus@ics.muni.cz>
|
||||
*/
|
||||
@Controller
|
||||
@SessionAttributes("authorizationRequest")
|
||||
public class PerunOAuthConfirmationController{
|
||||
|
||||
public static final String APPROVE = "approve";
|
||||
|
||||
private final OAuthConfirmationController oAuthConfirmationController;
|
||||
private final UserInfoService userInfoService;
|
||||
private final PerunOidcConfig perunOidcConfig;
|
||||
private final SystemScopeService scopeService;
|
||||
private final PerunScopeClaimTranslationService scopeClaimTranslationService;
|
||||
private final WebHtmlClasses htmlClasses;
|
||||
|
||||
@Autowired
|
||||
public PerunOAuthConfirmationController(OAuthConfirmationController oAuthConfirmationController,
|
||||
UserInfoService userInfoService,
|
||||
PerunOidcConfig perunOidcConfig,
|
||||
SystemScopeService scopeService,
|
||||
PerunScopeClaimTranslationService scopeClaimTranslationService,
|
||||
WebHtmlClasses htmlClasses)
|
||||
{
|
||||
this.oAuthConfirmationController = oAuthConfirmationController;
|
||||
this.userInfoService = userInfoService;
|
||||
this.perunOidcConfig = perunOidcConfig;
|
||||
this.scopeService = scopeService;
|
||||
this.scopeClaimTranslationService = scopeClaimTranslationService;
|
||||
this.htmlClasses = htmlClasses;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/oauth/confirm_access", params = { "client_id" })
|
||||
public String confirmAccess(Map<String, Object> model, HttpServletRequest req, Principal p)
|
||||
{
|
||||
AuthorizationRequest authRequest = (AuthorizationRequest)model.get("authorizationRequest");
|
||||
String result = oAuthConfirmationController.confirmAccess(model, p);
|
||||
if (result.equals(APPROVE) && !perunOidcConfig.getTheme().equalsIgnoreCase("default")) {
|
||||
model.remove("scopes");
|
||||
model.remove("claims");
|
||||
ClientDetailsEntity client = (ClientDetailsEntity) model.get("client");
|
||||
PerunUserInfo user = (PerunUserInfo) userInfoService.getByUsernameAndClientId(
|
||||
p.getName(), client.getClientId());
|
||||
ControllerUtils.setScopesAndClaims(scopeService, scopeClaimTranslationService, model, authRequest.getScope(),
|
||||
user);
|
||||
ControllerUtils.setPageOptions(model, req, htmlClasses, perunOidcConfig);
|
||||
|
||||
model.put("page", "consent");
|
||||
return "themedApprove";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -91,7 +91,7 @@ public class PerunUnapprovedRegistrationController {
|
|||
log.debug("groupsForReg: {}", groupsForRegistration);
|
||||
|
||||
if (groupsForRegistration.isEmpty()) {
|
||||
String redirectUrl = ControllerUtils.createRedirectUrl(request, REGISTRATION_FORM_MAPPING,
|
||||
String redirectUrl = ControllerUtils.createRedirectUrl(perunOidcConfig.getConfigBean().getIssuer(),
|
||||
PerunUnapprovedController.UNAPPROVED_MAPPING, Collections.singletonMap("client_id", clientId));
|
||||
response.reset();
|
||||
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
|
||||
|
|
|
@ -75,6 +75,15 @@ public class ConfigurationPropertiesBean {
|
|||
return issuer;
|
||||
}
|
||||
|
||||
public String getIssuer(boolean trailingSlash) {
|
||||
String iss = issuer.endsWith("/") ? issuer.substring(0, issuer.length() - 1) : issuer;
|
||||
if (trailingSlash) {
|
||||
return iss + '/';
|
||||
} else {
|
||||
return iss;
|
||||
}
|
||||
}
|
||||
|
||||
public void setIssuer(String iss) {
|
||||
issuer = iss;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue