Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Am 159 merge 3.19.2 into master #2247

Merged
merged 29 commits into from
Nov 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
329641c
chore: prepare next version
graviteeio Sep 27, 2022
3abd5a0
AM-67: removed set primary from email factor
Oct 6, 2022
0b6eb5d
AM-60: fixed Kafka audit reporter null pointer exception
Oct 7, 2022
e84fe88
fix: avoid overflow using integer divider onn timeMillis, prefer the …
leleueri Oct 11, 2022
fd15215
AM-69: fixed post logout query param issue
Oct 5, 2022
2107043
chore: prepare next version
graviteeio Oct 19, 2022
7730276
chore: prepare next version
graviteeio Oct 21, 2022
b8c5755
fix: use PostLogoutUrls defined at domain level if nothing is define …
leleueri Oct 17, 2022
b6d5f96
fix: check the validity of request_uri parameter when it is a URL
leleueri Oct 21, 2022
a64a34c
fix: do not restrict user claim coming from external idps to the stan…
leleueri Oct 19, 2022
a16f897
fix: group update using SCIM must preserve the roles assigned to the …
leleueri Oct 21, 2022
d1c4036
fix: do not limit the number of result during searchUser for forgot p…
leleueri Oct 19, 2022
725f501
AM-112 upgrade dependencies based on snyk scan
leleueri Oct 25, 2022
5df47b5
AM-118 manage email field during user update
leleueri Oct 25, 2022
587a589
AM-120 remove check on URL fragments for request_uri
leleueri Oct 26, 2022
b262283
AM-61 fix UI refresh issue
leleueri Oct 27, 2022
68c65f2
3.15.14
graviteeio Oct 28, 2022
3e6f6da
AM-113 Merge 3.15.14
leleueri Oct 28, 2022
95b7fa1
Merge pull request #2208 from gravitee-io/AM-113-merge-3.15.14
leleueri Nov 2, 2022
035ea0a
AM-128 initialize social idps to use them into the Register template
leleueri Oct 28, 2022
0379b29
fix: Allow secondary LDAP URL
farmborough Sep 28, 2022
8c40d55
AM-125 add forgotPassword action into identity first login page
leleueri Oct 31, 2022
54c332f
AM-66 Evaluation WebAuthn settings before reading the origin during F…
leleueri Nov 2, 2022
adef72f
3.18.10
graviteeio Nov 4, 2022
08a61e9
AM-114 Merge AM 3.18.10 into 3.19.x
leleueri Nov 9, 2022
2fd46ea
AM-119 hide the skip button on MFA enroll page if the MFA is required
leleueri Nov 9, 2022
c1e52f3
Merge pull request #2223 from gravitee-io/AM-114-merge-3.18.10
leleueri Nov 10, 2022
520f85c
3.19.2
graviteeio Nov 10, 2022
d941a18
AM-159 merge AM 3.19.2 into master
leleueri Nov 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,12 @@ public interface ConstantKeys {
// entry into the io.gravitee.am.model.AuthenticationFlowContext to get access to the
// content of the OAuth2 parameters retrieved using PAR
String REQUEST_PARAMETERS_KEY = "requestParameters";


String ALLOW_REGISTER_CONTEXT_KEY = "allowRegister";
String ALLOW_PASSWORDLESS_CONTEXT_KEY = "allowPasswordless";
String ALLOW_FORGOT_PASSWORD_CONTEXT_KEY = "allowForgotPassword";
String REGISTER_ACTION_KEY = "registerAction";
String WEBAUTHN_ACTION_KEY = "passwordlessAction";
String FORGOT_ACTION_KEY = "forgotPasswordAction";
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ private Completable generateCodeAndSendEmail(FactorContext context, EmailSenderP
return provider.sendMessage(emailWrapper.getEmail(), emailWrapper.isFromDefaultTemplate())
.andThen(Single.just(enrolledFactor)
.flatMap(ef -> {
ef.setPrimary(true);
ef.getSecurity().putData(FactorDataKeys.KEY_EXPIRE_AT, emailWrapper.getExpireAt());
return userService.addFactor(context.getUser().getId(), ef, new DefaultUser(context.getUser()));
}).ignoreElement());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,9 @@ private Email prepareEmail(io.gravitee.am.model.Template template, io.gravitee.a
private Map<String, Object> prepareEmailParams(User user, Client client, Integer expiresAfter, String redirectUri) {
// generate a JWT to store user's information and for security purpose
final Map<String, Object> claims = new HashMap<>();
claims.put(Claims.iat, new Date().getTime() / 1000);
claims.put(Claims.exp, new Date(System.currentTimeMillis() + (expiresAfter * 1000)).getTime() / 1000);
Instant now = Instant.now();
claims.put(Claims.iat, now.getEpochSecond());
claims.put(Claims.exp, now.plusSeconds(expiresAfter).getEpochSecond());
claims.put(Claims.sub, user.getId());
if (client != null) {
claims.put(Claims.aud, client.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@
package io.gravitee.am.gateway.handler.common.user.impl;

import io.gravitee.am.gateway.handler.common.user.UserService;
import io.gravitee.am.model.User;
import io.gravitee.am.model.ReferenceType;
import io.gravitee.am.model.User;
import io.gravitee.am.model.factor.EnrolledFactor;
import io.gravitee.am.repository.management.api.search.FilterCriteria;
import io.reactivex.Maybe;
import io.reactivex.Single;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;

/**
Expand Down Expand Up @@ -53,7 +52,7 @@ public Maybe<User> findByDomainAndUsernameAndSource(String domain, String userna

@Override
public Single<List<User>> findByDomainAndCriteria(String domain, FilterCriteria criteria) {
return userService.search(ReferenceType.DOMAIN, domain, criteria, 0, 2).map(p -> new ArrayList<>(p.getData()));
return userService.search(ReferenceType.DOMAIN, domain, criteria).toList();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ protected void doStart() throws Exception {
rootRouter.route(HttpMethod.GET, PATH_REGISTER)
.handler(clientRequestParseHandler)
.handler(registerAccessHandler)
.handler(new LoginSocialAuthenticationHandler(identityProviderManager, jwtService, certificateManager))
.handler(policyChainHandler.create(ExtensionPoint.PRE_REGISTER))
.handler(localeHandler)
.handler(new RegisterEndpoint(thymeleafTemplateEngine, domain, botDetectionManager));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.am.gateway.handler.oauth2.resources.handler.authorization;
package io.gravitee.am.gateway.handler.root.resources.endpoint;

import com.nimbusds.jwt.JWT;
import io.gravitee.am.common.oidc.Parameters;
Expand Down Expand Up @@ -104,8 +104,13 @@ public static boolean redirectMatches(String requestedRedirect, String registere
}
// else we use the enhanced wildcard feature
else if (nonNull(hostPattern) && requestHost.matches(hostPattern)) {
String pathPattern = buildPattern(registeredUrl.getPath());
return requestedUrl.getPath().matches(pathPattern);
final String requestedUrlPath = requestedUrl.getPath();
final String registeredUrlPath = registeredUrl.getPath();
if(requestedUrlPath.isEmpty() && (registeredUrlPath.isEmpty() || registeredUrlPath.startsWith("*") || registeredUrlPath.startsWith("/*"))) {
return true;
}
String pathPattern = buildPattern(registeredUrlPath);
return requestedUrlPath.matches(pathPattern);
}
}
} catch (MalformedURLException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,18 @@
import java.util.Objects;

import static io.gravitee.am.common.utils.ConstantKeys.ACTION_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.ALLOW_FORGOT_PASSWORD_CONTEXT_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.ALLOW_PASSWORDLESS_CONTEXT_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.ALLOW_REGISTER_CONTEXT_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.CLIENT_CONTEXT_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.DOMAIN_CONTEXT_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.ERROR_DESCRIPTION_PARAM_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.ERROR_PARAM_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.FORGOT_ACTION_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.PARAM_CONTEXT_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.REGISTER_ACTION_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.USERNAME_PARAM_KEY;
import static io.gravitee.am.common.utils.ConstantKeys.WEBAUTHN_ACTION_KEY;
import static io.gravitee.am.gateway.handler.common.utils.ThymeleafDataHelper.generateData;
import static io.gravitee.am.gateway.handler.common.vertx.utils.UriBuilderRequest.CONTEXT_PATH;
import static io.gravitee.am.gateway.handler.common.vertx.utils.UriBuilderRequest.resolveProxyRequest;
Expand All @@ -62,10 +68,7 @@ public class IdentifierFirstLoginEndpoint extends AbstractEndpoint implements Ha

private static final Logger logger = LoggerFactory.getLogger(IdentifierFirstLoginEndpoint.class);
private static final String REQUEST_CONTEXT_KEY = "request";
private static final String ALLOW_REGISTER_CONTEXT_KEY = "allowRegister";
private static final String ALLOW_PASSWORDLESS_CONTEXT_KEY = "allowPasswordless";
private static final String REGISTER_ACTION_KEY = "registerAction";
private static final String WEBAUTHN_ACTION_KEY = "passwordlessAction";


private final Domain domain;
private final BotDetectionManager botDetectionManager;
Expand Down Expand Up @@ -105,6 +108,7 @@ private void renderLoginPage(RoutingContext routingContext) {
// put login settings in context
LoginSettings loginSettings = LoginSettings.getInstance(domain, client);
var optionalSettings = ofNullable(loginSettings).filter(Objects::nonNull);
routingContext.put(ALLOW_FORGOT_PASSWORD_CONTEXT_KEY, optionalSettings.map(LoginSettings::isForgotPasswordEnabled).orElse(false));
routingContext.put(ALLOW_REGISTER_CONTEXT_KEY, optionalSettings.map(LoginSettings::isRegisterEnabled).orElse(false));
routingContext.put(ALLOW_PASSWORDLESS_CONTEXT_KEY, optionalSettings.map(LoginSettings::isPasswordlessEnabled).orElse(false));

Expand All @@ -129,6 +133,7 @@ private void renderLoginPage(RoutingContext routingContext) {
routingContext.put(ACTION_KEY, resolveProxyRequest(request, routingContext.get(CONTEXT_PATH) + "/login/identifier", queryParams, true));
routingContext.put(REGISTER_ACTION_KEY, UriBuilderRequest.resolveProxyRequest(routingContext.request(), routingContext.get(CONTEXT_PATH) + "/register", queryParams, true));
routingContext.put(WEBAUTHN_ACTION_KEY, UriBuilderRequest.resolveProxyRequest(routingContext.request(), routingContext.get(CONTEXT_PATH) + "/webauthn/login", queryParams, true));
routingContext.put(FORGOT_ACTION_KEY, resolveProxyRequest(routingContext.request(), routingContext.get(CONTEXT_PATH) + "/forgotPassword", queryParams, true));

final Map<String, Object> data = generateData(routingContext, domain, client);
data.putAll(botDetectionManager.getTemplateVariables(domain, client));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
import io.gravitee.am.common.jwt.Claims;
import io.gravitee.am.common.oidc.Parameters;
import io.gravitee.am.common.oidc.StandardClaims;
import io.gravitee.am.common.web.UriBuilder;
import io.gravitee.am.common.utils.ConstantKeys;
import io.gravitee.am.common.web.UriBuilder;
import io.gravitee.am.gateway.handler.common.vertx.utils.RequestUtils;
import io.gravitee.am.gateway.handler.root.resources.endpoint.ParamUtils;
import io.gravitee.am.gateway.handler.root.service.user.UserService;
import io.gravitee.am.identityprovider.api.DefaultUser;
import io.gravitee.am.model.Domain;
Expand All @@ -46,6 +47,8 @@
import static io.gravitee.am.service.impl.user.activity.utils.ConsentUtils.canSaveIp;
import static io.gravitee.am.service.impl.user.activity.utils.ConsentUtils.canSaveUserAgent;

import static org.springframework.util.CollectionUtils.isEmpty;

/**
* @author Eric LELEU (eric.leleu at graviteesource.com)
* @author GraviteeSource Team
Expand Down Expand Up @@ -124,9 +127,8 @@ protected void doRedirect(Client client, RoutingContext routingContext, String e
// The OP also MUST NOT perform post-logout redirection if the post_logout_redirect_uri value supplied
// does not exactly match one of the previously registered post_logout_redirect_uris values.
// if client is null, check security domain options
List<String> registeredUris = client != null ? client.getPostLogoutRedirectUris() :
(domain.getOidc() != null ? domain.getOidc().getPostLogoutRedirectUris() : null);
if (!isMatchingRedirectUri(logoutRedirectUrl, registeredUris)) {
List<String> registeredUris = client != null && !isEmpty(client.getPostLogoutRedirectUris()) ? client.getPostLogoutRedirectUris() : (domain.getOidc() != null ? domain.getOidc().getPostLogoutRedirectUris() : null);
if (!isMatchingRedirectUri(logoutRedirectUrl, registeredUris, domain.isRedirectUriStrictMatching() || domain.usePlainFapiProfile())) {
routingContext.fail(new InvalidRequestException("The post_logout_redirect_uri MUST match the registered callback URLs"));
return;
}
Expand Down Expand Up @@ -223,7 +225,7 @@ private void doRedirect0(RoutingContext routingContext, String url) {
}
}

private boolean isMatchingRedirectUri(String requestedRedirectUri, List<String> registeredRedirectUris) {
private boolean isMatchingRedirectUri(String requestedRedirectUri, List<String> registeredRedirectUris, boolean uriStrictMatch) {
// no registered uris to check, continue
if (registeredRedirectUris == null) {
return true;
Expand All @@ -239,7 +241,8 @@ private boolean isMatchingRedirectUri(String requestedRedirectUri, List<String>
// compare values
return registeredRedirectUris
.stream()
.anyMatch(registeredUri -> requestedRedirectUri.equals(registeredUri));
.anyMatch(registeredUri -> ParamUtils.redirectMatches(requestedRedirectUri, registeredUri, uriStrictMatch));

}

private io.gravitee.am.identityprovider.api.User getAuthenticatedUser(User endUser, RoutingContext routingContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
import static io.gravitee.am.gateway.handler.common.utils.ThymeleafDataHelper.generateData;
import static io.gravitee.am.gateway.handler.common.vertx.utils.UriBuilderRequest.CONTEXT_PATH;
import static io.gravitee.am.gateway.handler.common.vertx.utils.UriBuilderRequest.resolveProxyRequest;
import static io.gravitee.am.gateway.handler.root.resources.handler.webauthn.WebAuthnHandler.getOrigin;
import static io.gravitee.am.model.factor.FactorStatus.ACTIVATED;
import static io.gravitee.am.model.factor.FactorStatus.PENDING_ACTIVATION;
import static java.util.Optional.ofNullable;
Expand Down Expand Up @@ -254,7 +255,7 @@ private void verifyCode(RoutingContext routingContext) {
if(factor.is(FIDO2)){
factorData.put(ConstantKeys.PASSWORDLESS_CHALLENGE_KEY, routingContext.session().get(PASSWORDLESS_CHALLENGE_KEY));
factorData.put(ConstantKeys.PASSWORDLESS_CHALLENGE_USERNAME_KEY, routingContext.session().get(PASSWORDLESS_CHALLENGE_USERNAME_KEY));
factorData.put(ConstantKeys.PASSWORDLESS_ORIGIN, domain.getWebAuthnSettings().getOrigin());
factorData.put(ConstantKeys.PASSWORDLESS_ORIGIN, getOrigin(domain.getWebAuthnSettings()));
}

final FactorContext factorCtx = new FactorContext(applicationContext, factorData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.gravitee.am.model.*;
import io.gravitee.am.model.factor.EnrolledFactor;
import io.gravitee.am.model.factor.EnrolledFactorSecurity;
import io.gravitee.am.model.login.WebAuthnSettings;
import io.gravitee.am.model.oidc.Client;
import io.gravitee.am.service.CredentialService;
import io.gravitee.am.service.FactorService;
Expand Down Expand Up @@ -61,7 +62,7 @@
* @author GraviteeSource Team
*/
public abstract class WebAuthnHandler extends AbstractEndpoint implements Handler<RoutingContext> {

private static final String DEFAULT_ORIGIN = "http://localhost:8092";
private static final Logger logger = LoggerFactory.getLogger(WebAuthnHandler.class);
private FactorManager factorManager;
private FactorService factorService;
Expand Down Expand Up @@ -261,4 +262,10 @@ protected void updateCredential(AuthenticationContext authenticationContext, Str
);
}

public static String getOrigin(WebAuthnSettings settings) {
return (settings!= null
&& settings.getOrigin() != null) ?
settings.getOrigin() :
DEFAULT_ORIGIN;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,7 @@ public WebAuthnLoginHandler(FactorService factorService,
setUserAuthenticationManager(userAuthenticationManager);
setDomain(domain);
this.webAuthn = webAuthn;
this.origin = (domain.getWebAuthnSettings() != null
&& domain.getWebAuthnSettings().getOrigin() != null) ?
domain.getWebAuthnSettings().getOrigin() :
DEFAULT_ORIGIN;
this.origin = getOrigin(domain.getWebAuthnSettings());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,7 @@ public WebAuthnRegisterHandler(FactorService factorService,
setCredentialService(credentialService);
setDomain(domain);
this.webAuthn = webAuthn;
this.origin = (domain.getWebAuthnSettings() != null
&& domain.getWebAuthnSettings().getOrigin() != null) ?
domain.getWebAuthnSettings().getOrigin() :
DEFAULT_ORIGIN;
this.origin = getOrigin(domain.getWebAuthnSettings());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,7 @@ public WebAuthnResponseHandler(FactorService factorService,
setUserAuthenticationManager(userAuthenticationManager);
setDomain(domain);
this.webAuthn = webAuthn;
this.origin = (domain.getWebAuthnSettings() != null
&& domain.getWebAuthnSettings().getOrigin() != null) ?
domain.getWebAuthnSettings().getOrigin() :
DEFAULT_ORIGIN;
this.origin = getOrigin(domain.getWebAuthnSettings());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
<!-- END OF STEP 2 -->
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<div class="section">
<button type="submit" name="user_mfa_enrollment" value="false" class="button secondary" th:text="#{mfa_enroll.button.skip}"></button>
<button th:if="${mfa_force_enrollment == false}" type="submit" name="user_mfa_enrollment" value="false" class="button secondary" th:text="#{mfa_enroll.button.skip}"></button>
<a id="previous" class="hide" href="#"><span class="icons">arrow_back</span><span th:text="#{mfa_enroll.button.back}"/></a>
</div>
</form>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.gravitee.am.gateway.handler.root.resources.endpoint;

import org.junit.Test;

import static org.junit.Assert.*;

public class ParamUtilsTest {

@Test
public void redirectMatch_url_with_path_success() {
final String requestRedirectUri = "https://test.com/department/business";
final String registeredRedirectUri = "https://test.com/department/*";

boolean matched = ParamUtils.redirectMatches(requestRedirectUri, registeredRedirectUri, false);
assertTrue( matched);
}

@Test
public void redirectMatch_url_with_path_fail() {
final String requestRedirectUri = "https://test.com/other/business";
final String registeredRedirectUri = "https://test.com/department/*";

boolean matched = ParamUtils.redirectMatches(requestRedirectUri, registeredRedirectUri, false);
assertFalse( matched);
}

@Test
public void redirectMatch_url_without_path_success() {
final String requestRedirectUri = "https://test.com?id=10";
final String registeredRedirectUri1 = "https://test.com/*";

assertTrue(ParamUtils.redirectMatches(requestRedirectUri, registeredRedirectUri1, false));

final String registeredRedirectUri2 = "https://test.com*";
assertTrue(ParamUtils.redirectMatches(requestRedirectUri, registeredRedirectUri2, false));
}

@Test
public void redirectMatch_url_path_with_param_success() {
final String requestRedirectUri = "https://test.com/department?id=10";
final String registeredRedirectUri = "https://test.com/department*";

boolean matched = ParamUtils.redirectMatches(requestRedirectUri, registeredRedirectUri, false);
assertTrue( matched);
}

@Test
public void redirectMatch_url_without_path_fail() {
final String requestRedirectUri = "https://test.com?id=10";
final String registeredRedirectUri = "https://test.com/department*";

boolean matched = ParamUtils.redirectMatches(requestRedirectUri, registeredRedirectUri, false);
assertFalse( matched);
}

@Test
public void redirectMatch_url_with_param_strict_fail() {
final String requestRedirectUri = "https://test.com?id=10";
final String registeredRedirectUri = "https://test.com";

assertFalse(ParamUtils.redirectMatches(requestRedirectUri, registeredRedirectUri, true));

final String registeredRedirectUriWildCard = "https://test.com*";
assertFalse(ParamUtils.redirectMatches(requestRedirectUri, registeredRedirectUriWildCard, true));

final String requestRedirectUriParam = "https://test.com/people";
assertFalse(ParamUtils.redirectMatches(requestRedirectUriParam, registeredRedirectUri, true));

final String requestRedirectUriParamQuery = "https://test.com/people?v=123";
assertFalse(ParamUtils.redirectMatches(requestRedirectUriParamQuery, registeredRedirectUri, true));
}
}
Loading