diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2KeycloakAuthConfig.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2KeycloakAuthConfig.java new file mode 100644 index 000000000..a6395f972 --- /dev/null +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2KeycloakAuthConfig.java @@ -0,0 +1,50 @@ +package org.lowcoder.sdk.auth; + +import static org.lowcoder.sdk.auth.constants.Oauth2Constants.INSTANCE_ID_PLACEHOLDER; +import static org.lowcoder.sdk.auth.constants.Oauth2Constants.REALM_PLACEHOLDER; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Getter; + +/** + * + * Keycloak OAuth configuration. + */ +@Getter +public class Oauth2KeycloakAuthConfig extends Oauth2SimpleAuthConfig +{ + protected String instanceId; + protected String realm; + + @JsonCreator + public Oauth2KeycloakAuthConfig( + @JsonProperty("id") String id, + @JsonProperty("enable") Boolean enable, + @JsonProperty("enableRegister") Boolean enableRegister, + @JsonProperty("source") String source, + @JsonProperty("sourceName") String sourceName, + @JsonProperty("clientId") String clientId, + @JsonProperty("clientSecret") String clientSecret, + @JsonProperty("instanceId") String instanceId, + @JsonProperty("realm") String realm, + @JsonProperty("authType") String authType) + { + super(id, enable, enableRegister, source, sourceName, clientId, clientSecret, authType); + this.instanceId = instanceId; + this.realm = realm; + } + + + + @Override + public String replaceAuthUrlClientIdPlaceholder(String url) + { + return super.replaceAuthUrlClientIdPlaceholder(url) + .replace(INSTANCE_ID_PLACEHOLDER, instanceId) + .replace(REALM_PLACEHOLDER, realm); + } + + +} diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2OryAuthConfig.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2OryAuthConfig.java index 8082a62cb..345e05b96 100644 --- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2OryAuthConfig.java +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2OryAuthConfig.java @@ -1,18 +1,12 @@ package org.lowcoder.sdk.auth; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonView; -import lombok.Getter; -import org.apache.commons.lang3.StringUtils; -import org.lowcoder.sdk.auth.constants.AuthTypeConstants; -import org.lowcoder.sdk.auth.constants.Oauth2Constants; -import org.lowcoder.sdk.config.SerializeConfig.JsonViews; +import static org.lowcoder.sdk.auth.constants.Oauth2Constants.INSTANCE_ID_PLACEHOLDER; import javax.annotation.Nullable; -import java.util.function.Function; -import static org.lowcoder.sdk.auth.constants.Oauth2Constants.CLIENT_ID_PLACEHOLDER; -import static org.lowcoder.sdk.auth.constants.Oauth2Constants.INSTANCE_ID_PLACEHOLDER; +import com.fasterxml.jackson.annotation.JsonCreator; + +import lombok.Getter; /** * OAuth2 ORY auth config. diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2SimpleAuthConfig.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2SimpleAuthConfig.java index 7ab980785..907edbb1c 100644 --- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2SimpleAuthConfig.java +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2SimpleAuthConfig.java @@ -53,6 +53,7 @@ public String getAuthorizeUrl() { case AuthTypeConstants.GOOGLE -> replaceAuthUrlClientIdPlaceholder(Oauth2Constants.GOOGLE_AUTHORIZE_URL); case AuthTypeConstants.GITHUB -> replaceAuthUrlClientIdPlaceholder(Oauth2Constants.GITHUB_AUTHORIZE_URL); case AuthTypeConstants.ORY -> replaceAuthUrlClientIdPlaceholder(Oauth2Constants.ORY_AUTHORIZE_URL); + case AuthTypeConstants.KEYCLOAK -> replaceAuthUrlClientIdPlaceholder(Oauth2Constants.KEYCLOAK_AUTHORIZE_URL); default -> null; }; } diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/AuthTypeConstants.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/AuthTypeConstants.java index bed41b4f8..f2d4076bd 100644 --- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/AuthTypeConstants.java +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/AuthTypeConstants.java @@ -9,4 +9,5 @@ public class AuthTypeConstants { public static final String GOOGLE = "GOOGLE"; public static final String GITHUB = "GITHUB"; public static final String ORY = "ORY"; + public static final String KEYCLOAK = "KEYCLOAK"; } diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/Oauth2Constants.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/Oauth2Constants.java index c6e686322..6313af520 100644 --- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/Oauth2Constants.java +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/Oauth2Constants.java @@ -6,8 +6,9 @@ public class Oauth2Constants { public static final String CLIENT_ID_PLACEHOLDER = "$CLIENT_ID"; public static final String REDIRECT_URL_PLACEHOLDER = "$REDIRECT_URL"; public static final String STATE_PLACEHOLDER = "$STATE"; + public static final String REALM_PLACEHOLDER = "$REALM"; - public static final String INSTANCE_ID_PLACEHOLDER = "INSTANCE_ID"; + public static final String INSTANCE_ID_PLACEHOLDER = "$INSTANCE_ID"; // authorize url public static final String GITHUB_AUTHORIZE_URL = "https://github.com/login/oauth/authorize" @@ -32,4 +33,11 @@ public class Oauth2Constants { + "&redirect_uri=" + REDIRECT_URL_PLACEHOLDER + "&state=" + STATE_PLACEHOLDER + "&scope=openid email profile offline_access"; + + public static final String KEYCLOAK_AUTHORIZE_URL = "https://" + INSTANCE_ID_PLACEHOLDER + "/realms/" + REALM_PLACEHOLDER + "/protocol/openid-connect/auth" + + "?response_type=code" + + "&client_id=" + CLIENT_ID_PLACEHOLDER + + "&redirect_uri=" + REDIRECT_URL_PLACEHOLDER + + "&state=" + STATE_PLACEHOLDER + + "&scope=openid email profile"; } diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/constants/AuthSourceConstants.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/constants/AuthSourceConstants.java index 073d333d0..c82d42d21 100644 --- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/constants/AuthSourceConstants.java +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/constants/AuthSourceConstants.java @@ -11,11 +11,13 @@ public class AuthSourceConstants { public static final String GOOGLE = "GOOGLE"; public static final String GITHUB = "GITHUB"; public static final String ORY = "ORY"; + public static final String KEYCLOAK = "KEYCLOAK"; // source name public static final String GOOGLE_NAME = "Google"; public static final String GITHUB_NAME = "Github"; public static final String ORY_NAME = "Ory"; + public static final String KEYCLOAK_NAME = "Keycloak"; // default source and source name for common protocol // oauth 2.0 diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/util/JsonUtils.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/util/JsonUtils.java index eedb3fe42..19e52ed60 100644 --- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/util/JsonUtils.java +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/util/JsonUtils.java @@ -8,6 +8,7 @@ import javax.annotation.Nullable; import org.lowcoder.sdk.auth.EmailAuthConfig; +import org.lowcoder.sdk.auth.Oauth2KeycloakAuthConfig; import org.lowcoder.sdk.auth.Oauth2OryAuthConfig; import org.lowcoder.sdk.auth.Oauth2SimpleAuthConfig; @@ -46,6 +47,7 @@ public final class JsonUtils { OBJECT_MAPPER.registerSubtypes(new NamedType(Oauth2SimpleAuthConfig.class, GITHUB)); OBJECT_MAPPER.registerSubtypes(new NamedType(Oauth2SimpleAuthConfig.class, GOOGLE)); OBJECT_MAPPER.registerSubtypes(new NamedType(Oauth2OryAuthConfig.class, ORY)); + OBJECT_MAPPER.registerSubtypes(new NamedType(Oauth2KeycloakAuthConfig.class, KEYCLOAK)); } public static final JsonNode EMPTY_JSON_NODE = createObjectNode(); diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java index 0d2829fff..7017da2fc 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java @@ -1,7 +1,7 @@ package org.lowcoder.api.authentication; -import com.fasterxml.jackson.annotation.JsonView; -import lombok.extern.slf4j.Slf4j; +import java.util.List; + import org.lowcoder.api.authentication.dto.AuthConfigRequest; import org.lowcoder.api.authentication.service.AuthenticationApiService; import org.lowcoder.api.framework.view.ResponseView; @@ -9,7 +9,6 @@ import org.lowcoder.api.usermanagement.UserController; import org.lowcoder.api.usermanagement.UserController.UpdatePasswordRequest; import org.lowcoder.api.util.BusinessEventPublisher; -import org.lowcoder.domain.authentication.AuthenticationService; import org.lowcoder.domain.authentication.FindAuthConfig; import org.lowcoder.infra.constant.NewUrl; import org.lowcoder.sdk.auth.AbstractAuthConfig; @@ -17,13 +16,20 @@ import org.lowcoder.sdk.constants.AuthSourceConstants; import org.lowcoder.sdk.util.CookieHelper; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; -import java.util.List; +import com.fasterxml.jackson.annotation.JsonView; + +import reactor.core.publisher.Mono; -@Slf4j @RestController @RequestMapping(value = {NewUrl.CUSTOM_AUTH}) public class AuthenticationController { @@ -36,8 +42,6 @@ public class AuthenticationController { private CookieHelper cookieHelper; @Autowired private BusinessEventPublisher businessEventPublisher; - @Autowired - private AuthenticationService authenticationService; /** * login by email or phone with password; or register by email for now. diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2AuthRequestFactory.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2AuthRequestFactory.java index 84794e494..c8fdae9b2 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2AuthRequestFactory.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2AuthRequestFactory.java @@ -7,7 +7,9 @@ import org.lowcoder.api.authentication.request.oauth2.request.AbstractOauth2Request; import org.lowcoder.api.authentication.request.oauth2.request.GithubRequest; import org.lowcoder.api.authentication.request.oauth2.request.GoogleRequest; +import org.lowcoder.api.authentication.request.oauth2.request.KeycloakRequest; import org.lowcoder.api.authentication.request.oauth2.request.OryRequest; +import org.lowcoder.sdk.auth.Oauth2KeycloakAuthConfig; import org.lowcoder.sdk.auth.Oauth2OryAuthConfig; import org.lowcoder.sdk.auth.Oauth2SimpleAuthConfig; import org.springframework.stereotype.Component; @@ -29,6 +31,7 @@ private AbstractOauth2Request buildRequest(OAu case GITHUB -> new GithubRequest((Oauth2SimpleAuthConfig) context.getAuthConfig()); case GOOGLE -> new GoogleRequest((Oauth2SimpleAuthConfig) context.getAuthConfig()); case ORY -> new OryRequest((Oauth2OryAuthConfig) context.getAuthConfig()); + case KEYCLOAK -> new KeycloakRequest((Oauth2KeycloakAuthConfig)context.getAuthConfig()); default -> throw new UnsupportedOperationException(context.getAuthConfig().getAuthType()); }; } @@ -38,6 +41,7 @@ public Set supportedAuthTypes() { return Set.of( GITHUB, GOOGLE, - ORY); + ORY, + KEYCLOAK); } } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2DefaultSource.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2DefaultSource.java index c0242d153..70a7cc776 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2DefaultSource.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2DefaultSource.java @@ -1,6 +1,7 @@ package org.lowcoder.api.authentication.request.oauth2; import org.lowcoder.sdk.auth.constants.Oauth2Constants; +import org.springframework.security.config.oauth2.client.CommonOAuth2Provider; public enum Oauth2DefaultSource implements Oauth2Source { @@ -17,7 +18,7 @@ public String userInfo() { @Override public String refresh() { - return "https://www.googleapis.com/oauth2/v4/token"; + return "https://github.com/login/oauth/access_token"; } }, @@ -55,5 +56,24 @@ public String refresh() { return "https://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/oauth2/token"; } + }, + + KEYCLOAK { + + @Override + public String accessToken() { + return "http://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/realms/" + Oauth2Constants.REALM_PLACEHOLDER + "/protocol/openid-connect/token"; + } + + @Override + public String userInfo() { + return "http://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/realms/" + Oauth2Constants.REALM_PLACEHOLDER + "/protocol/openid-connect/userinfo"; + } + + @Override + public String refresh() { + return "http://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/realms/" + Oauth2Constants.REALM_PLACEHOLDER + "/protocol/openid-connect/token"; + } + } } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/request/KeycloakRequest.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/request/KeycloakRequest.java new file mode 100644 index 000000000..31dcd650d --- /dev/null +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/request/KeycloakRequest.java @@ -0,0 +1,125 @@ +package org.lowcoder.api.authentication.request.oauth2.request; + +import static org.springframework.web.reactive.function.BodyInserters.fromFormData; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +import org.apache.commons.collections4.MapUtils; +import org.apache.http.client.utils.URIBuilder; +import org.lowcoder.api.authentication.request.AuthException; +import org.lowcoder.api.authentication.request.oauth2.OAuth2RequestContext; +import org.lowcoder.api.authentication.request.oauth2.Oauth2DefaultSource; +import org.lowcoder.domain.user.model.AuthToken; +import org.lowcoder.domain.user.model.AuthUser; +import org.lowcoder.sdk.auth.Oauth2KeycloakAuthConfig; +import org.lowcoder.sdk.util.JsonUtils; +import org.lowcoder.sdk.webclient.WebClientBuildHelper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.MediaType; + +import reactor.core.publisher.Mono; + +public class KeycloakRequest extends AbstractOauth2Request +{ + + public KeycloakRequest(Oauth2KeycloakAuthConfig config) + { + super(config, Oauth2DefaultSource.KEYCLOAK); + } + + @Override + protected Mono getAuthToken(OAuth2RequestContext context) { + URI uri; + try { + uri = new URIBuilder(config.replaceAuthUrlClientIdPlaceholder(source.accessToken())).build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + return WebClientBuildHelper.builder() + .systemProxy() + .build() + .post() + .uri(uri) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .body(fromFormData("code", context.getCode()) + .with("client_id", config.getClientId()) + .with("client_secret", config.getClientSecret()) + .with("grant_type", "authorization_code") + .with("redirect_uri", context.getRedirectUrl())) + .exchangeToMono(response -> response.bodyToMono(new ParameterizedTypeReference>() { + })) + .flatMap(map -> { + if (map.containsKey("error") || map.containsKey("error_description")) { + throw new AuthException(JsonUtils.toJson(map)); + } + AuthToken authToken = AuthToken.builder() + .accessToken(MapUtils.getString(map, "access_token")) + .expireIn(MapUtils.getIntValue(map, "expires_in")) + .refreshToken(MapUtils.getString(map, "refresh_token")) + .build(); + return Mono.just(authToken); + }); + } + + @Override + protected Mono refreshAuthToken(String refreshToken) { + + URI uri; + try { + uri = new URIBuilder(config.replaceAuthUrlClientIdPlaceholder(source.refresh())).build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + return WebClientBuildHelper.builder() + .systemProxy() + .build() + .post() + .uri(uri) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .body(fromFormData("refresh_token", refreshToken) + .with("client_id", config.getClientId()) + .with("client_secret", config.getClientSecret()) + .with("grant_type", "refresh_token")) + .exchangeToMono(response -> response.bodyToMono(new ParameterizedTypeReference>() { + })) + .flatMap(map -> { + if (map.containsKey("error") || map.containsKey("error_description")) { + throw new AuthException(JsonUtils.toJson(map)); + } + AuthToken authToken = AuthToken.builder() + .accessToken(MapUtils.getString(map, "access_token")) + .expireIn(MapUtils.getIntValue(map, "expires_in")) + .refreshToken(MapUtils.getString(map, "refresh_token")) + .build(); + return Mono.just(authToken); + }); + + } + + @Override + protected Mono getAuthUser(AuthToken authToken) { + return WebClientBuildHelper.builder() + .systemProxy() + .build() + .post() + .uri(config.replaceAuthUrlClientIdPlaceholder(source.userInfo())) + .header("Authorization", "Bearer " + authToken.getAccessToken()) + .exchangeToMono(response -> response.bodyToMono(new ParameterizedTypeReference>() { + })) + .flatMap(map -> { + if (map.containsKey("error") || map.containsKey("error_description")) { + throw new AuthException(JsonUtils.toJson(map)); + } + AuthUser authUser = AuthUser.builder() + .uid(MapUtils.getString(map, "sub")) + .username(MapUtils.getString(map, "name")) + .rawUserInfo(map) + .build(); + return Mono.just(authUser); + }); + } +} diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/factory/AuthConfigFactoryImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/factory/AuthConfigFactoryImpl.java index b5434c8e0..82a8b331a 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/factory/AuthConfigFactoryImpl.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/factory/AuthConfigFactoryImpl.java @@ -1,19 +1,23 @@ package org.lowcoder.api.authentication.service.factory; +import static java.util.Objects.requireNonNull; +import static org.lowcoder.sdk.constants.AuthSourceConstants.GITHUB; +import static org.lowcoder.sdk.constants.AuthSourceConstants.GITHUB_NAME; +import static org.lowcoder.sdk.constants.AuthSourceConstants.GOOGLE; +import static org.lowcoder.sdk.constants.AuthSourceConstants.GOOGLE_NAME; + +import java.util.Set; + import org.apache.commons.collections4.MapUtils; import org.lowcoder.api.authentication.dto.AuthConfigRequest; import org.lowcoder.sdk.auth.AbstractAuthConfig; import org.lowcoder.sdk.auth.EmailAuthConfig; +import org.lowcoder.sdk.auth.Oauth2KeycloakAuthConfig; import org.lowcoder.sdk.auth.Oauth2OryAuthConfig; import org.lowcoder.sdk.auth.Oauth2SimpleAuthConfig; import org.lowcoder.sdk.auth.constants.AuthTypeConstants; import org.springframework.stereotype.Component; -import java.util.Set; - -import static java.util.Objects.requireNonNull; -import static org.lowcoder.sdk.constants.AuthSourceConstants.*; - @Component public class AuthConfigFactoryImpl implements AuthConfigFactory { @@ -24,6 +28,7 @@ public AbstractAuthConfig build(AuthConfigRequest authConfigRequest, boolean ena case AuthTypeConstants.GITHUB -> buildOauth2SimpleAuthConfig(GITHUB, GITHUB_NAME, authConfigRequest, enable); case AuthTypeConstants.GOOGLE -> buildOauth2SimpleAuthConfig(GOOGLE, GOOGLE_NAME, authConfigRequest, enable); case AuthTypeConstants.ORY -> buildOauth2OryAuthConfig(authConfigRequest, enable); + case AuthTypeConstants.KEYCLOAK -> buildOauth2KeycloakAuthConfig(authConfigRequest, enable); default -> throw new UnsupportedOperationException(authConfigRequest.getAuthType()); }; } @@ -34,7 +39,8 @@ public Set supportAuthTypes() { AuthTypeConstants.FORM, AuthTypeConstants.GITHUB, AuthTypeConstants.GOOGLE, - AuthTypeConstants.ORY + AuthTypeConstants.ORY, + AuthTypeConstants.KEYCLOAK ); } @@ -68,4 +74,19 @@ private Oauth2SimpleAuthConfig buildOauth2OryAuthConfig(AuthConfigRequest authCo authConfigRequest.getInstanceId(), authConfigRequest.getAuthType()); } + + private Oauth2SimpleAuthConfig buildOauth2KeycloakAuthConfig(AuthConfigRequest authConfigRequest, boolean enable) { + return new Oauth2KeycloakAuthConfig( + authConfigRequest.getId(), + enable, + authConfigRequest.isEnableRegister(), + AuthTypeConstants.KEYCLOAK, + org.lowcoder.sdk.constants.AuthSourceConstants.KEYCLOAK_NAME, + requireNonNull(authConfigRequest.getClientId(), "clientId can not be null."), + authConfigRequest.getClientSecret(), + authConfigRequest.getInstanceId(), + authConfigRequest.getString("realm"), + authConfigRequest.getAuthType()); + } + } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/security/SecurityConfig.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/security/SecurityConfig.java index f7221862a..6f3f2f211 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/security/SecurityConfig.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/security/SecurityConfig.java @@ -15,6 +15,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.SecurityWebFiltersOrder; @@ -66,27 +67,24 @@ public class SecurityConfig { AuthRequestFactory authRequestFactory; @Bean - public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { + SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { if (!commonConfig.getSecurity().getForbiddenEndpoints().isEmpty()) { - http.authorizeExchange() - .matchers( - commonConfig.getSecurity().getForbiddenEndpoints().stream() + http.authorizeExchange(customizer -> customizer + .matchers(commonConfig.getSecurity().getForbiddenEndpoints().stream() .map(apiEndpoint -> ServerWebExchangeMatchers.pathMatchers(apiEndpoint.getMethod(), apiEndpoint.getUri())) .toArray(size -> new ServerWebExchangeMatcher[size]) - ).denyAll(); + ).denyAll() + ); } - http.cors() - .configurationSource(buildCorsConfigurationSource()) - .and() - .csrf().disable() - .anonymous().principal(createAnonymousUser()) - .and() - .httpBasic() - .and() - .authorizeExchange() + http + .cors(cors -> cors.configurationSource(buildCorsConfigurationSource())) + .csrf(csrf -> csrf.disable()) + .anonymous(anonymous -> anonymous.principal(createAnonymousUser())) + .httpBasic(Customizer.withDefaults()) + .authorizeExchange(customizer -> customizer .matchers( ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, CUSTOM_AUTH + "/otp/send"), // sms verification ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, CUSTOM_AUTH + "/phone/login"), @@ -134,19 +132,21 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, NewUrl.DATASOURCE_URL + "/jsDatasourcePlugins"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/api/docs/**") ) - .permitAll() + .permitAll() .pathMatchers("/api/**") - .authenticated() + .authenticated() .pathMatchers("/test/**") - .authenticated() + .authenticated() .pathMatchers("/**") - .permitAll() + .permitAll() .anyExchange() - .authenticated(); + .authenticated() + ); - http.exceptionHandling() + http.exceptionHandling(customizer -> customizer .authenticationEntryPoint(serverAuthenticationEntryPoint) - .accessDeniedHandler(accessDeniedHandler); + .accessDeniedHandler(accessDeniedHandler) + ); http.addFilterBefore(new UserSessionPersistenceFilter(sessionUserService, cookieHelper, authenticationService, authenticationApiService, authRequestFactory), SecurityWebFiltersOrder.AUTHENTICATION);