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

BE: Impl custom authentication page #635

Merged
merged 10 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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 @@ -18,7 +18,8 @@ protected AbstractAuthSecurityConfig() {
"/login",
"/logout",
"/oauth2/**",
"/static/**"
"/static/**",
"/api/config/authentication"
};

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.kafbat.ui.api.ApplicationConfigApi;
import io.kafbat.ui.config.ClustersProperties;
import io.kafbat.ui.model.ActionDTO;
import io.kafbat.ui.model.AppAuthenticationSettingsDTO;
import io.kafbat.ui.model.ApplicationConfigDTO;
import io.kafbat.ui.model.ApplicationConfigPropertiesDTO;
import io.kafbat.ui.model.ApplicationConfigValidationDTO;
Expand Down Expand Up @@ -66,6 +67,13 @@ public Mono<ResponseEntity<ApplicationInfoDTO>> getApplicationInfo(ServerWebExch
return Mono.just(applicationInfoService.getApplicationInfo()).map(ResponseEntity::ok);
}

@Override
public Mono<ResponseEntity<AppAuthenticationSettingsDTO>> getAuthenticationSettings(
ServerWebExchange exchange) {
return Mono.just(applicationInfoService.getAuthenticationProperties())
.map(ResponseEntity::ok);
}

@Override
public Mono<ResponseEntity<ApplicationConfigDTO>> getCurrentConfig(ServerWebExchange exchange) {
var context = AccessContext.builder()
Expand Down Expand Up @@ -109,7 +117,7 @@ public Mono<ResponseEntity<UploadedFileInfoDTO>> uploadConfigRelatedFile(Flux<Pa
.then(fileFlux.single())
.flatMap(file ->
dynamicConfigOperations.uploadConfigRelatedFile((FilePart) file)
.map(path -> new UploadedFileInfoDTO().location(path.toString()))
.map(path -> new UploadedFileInfoDTO(path.toString()))
.map(ResponseEntity::ok))
.doOnEach(sig -> audit(context, sig));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
@RestController
@RequiredArgsConstructor
@Slf4j
public class AuthController {
public class AuthenticationController {

@GetMapping(value = "/auth", produces = {"text/html"})
public Mono<byte[]> getAuth(ServerWebExchange exchange) {
Mono<CsrfToken> token = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());
return token
.map(AuthController::csrfToken)
.map(AuthenticationController::csrfToken)
.defaultIfEmpty("")
.map(csrfTokenHtmlInput -> createPage(exchange, csrfTokenHtmlInput));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
@RestController
@RequiredArgsConstructor
@Slf4j
public class AccessController implements AuthorizationApi {
public class AuthorizationController implements AuthorizationApi {

private final AccessControlService accessControlService;

Expand Down
46 changes: 46 additions & 0 deletions api/src/main/java/io/kafbat/ui/service/ApplicationInfoService.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,51 @@
package io.kafbat.ui.service;

import static io.kafbat.ui.api.model.AuthType.DISABLED;
import static io.kafbat.ui.api.model.AuthType.OAUTH2;
import static io.kafbat.ui.model.ApplicationInfoDTO.EnabledFeaturesEnum;
import static io.kafbat.ui.util.GithubReleaseInfo.GITHUB_RELEASE_INFO_TIMEOUT;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Streams;
import io.kafbat.ui.model.AppAuthenticationSettingsDTO;
import io.kafbat.ui.model.ApplicationInfoBuildDTO;
import io.kafbat.ui.model.ApplicationInfoDTO;
import io.kafbat.ui.model.ApplicationInfoLatestReleaseDTO;
import io.kafbat.ui.model.AuthTypeDTO;
import io.kafbat.ui.model.OAuthProviderDTO;
import io.kafbat.ui.util.DynamicConfigOperations;
import io.kafbat.ui.util.GithubReleaseInfo;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.info.BuildProperties;
import org.springframework.boot.info.GitProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.stereotype.Service;

@Service
public class ApplicationInfoService {
private final GithubReleaseInfo githubReleaseInfo;
private final ApplicationContext applicationContext;
private final DynamicConfigOperations dynamicConfigOperations;
private final BuildProperties buildProperties;
private final GitProperties gitProperties;

public ApplicationInfoService(DynamicConfigOperations dynamicConfigOperations,
ApplicationContext applicationContext,
@Autowired(required = false) BuildProperties buildProperties,
@Autowired(required = false) GitProperties gitProperties,
@Value("${" + GITHUB_RELEASE_INFO_TIMEOUT + ":10}") int githubApiMaxWaitTime) {
this.applicationContext = applicationContext;
this.dynamicConfigOperations = dynamicConfigOperations;
this.buildProperties = Optional.ofNullable(buildProperties).orElse(new BuildProperties(new Properties()));
this.gitProperties = Optional.ofNullable(gitProperties).orElse(new GitProperties(new Properties()));
Expand Down Expand Up @@ -70,6 +84,38 @@ private List<EnabledFeaturesEnum> getEnabledFeatures() {
return enabledFeatures;
}

public AppAuthenticationSettingsDTO getAuthenticationProperties() {
return new AppAuthenticationSettingsDTO()
.authType(AuthTypeDTO.fromValue(getAuthType()))
.oAuthProviders(getOAuthProviders());
}

private String getAuthType() {
return Optional.ofNullable(applicationContext.getEnvironment().getProperty("auth.type"))
.orElse(DISABLED.getValue());
}

@SuppressWarnings("unchecked")
private List<OAuthProviderDTO> getOAuthProviders() {
if (!getAuthType().equalsIgnoreCase(OAUTH2.getValue())) {
return Collections.emptyList();
}
var type = ResolvableType.forClassWithGenerics(Iterable.class, ClientRegistration.class);
String[] names = this.applicationContext.getBeanNamesForType(type);
var bean = (Iterable<ClientRegistration>) (names.length == 1 ? this.applicationContext.getBean(names[0]) : null);

if (bean == null) {
return Collections.emptyList();
}

return Streams.stream(bean.iterator())
.filter(r -> AuthorizationGrantType.AUTHORIZATION_CODE.equals(r.getAuthorizationGrantType()))
.map(r -> new OAuthProviderDTO()
.clientName(r.getClientName())
.authorizationUri("/oauth2/authorization/" + r.getRegistrationId()))
.toList();
}

// updating on startup and every hour
@Scheduled(fixedRateString = "${github-release-info-update-rate:3600000}")
public void updateGithubReleaseInfo() {
Expand Down
42 changes: 41 additions & 1 deletion contract/src/main/resources/swagger/kafbat-ui-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2150,7 +2150,7 @@ paths:
get:
tags:
- Authorization
summary: Get user authentication related info
summary: Get user authorization related info
operationId: getUserAuthInfo
responses:
200:
Expand Down Expand Up @@ -2244,6 +2244,20 @@ paths:
schema:
$ref: '#/components/schemas/UploadedFileInfo'

/api/config/authentication:
get:
tags:
- ApplicationConfig
summary: Get authentication methods enabled for the app and other related settings
operationId: getAuthenticationSettings
responses:
200:
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/AppAuthenticationSettings'

components:
schemas:
TopicSerdeSuggestion:
Expand Down Expand Up @@ -2354,6 +2368,32 @@ components:
htmlUrl:
type: string

AppAuthenticationSettings:
type: object
properties:
authType:
$ref: '#/components/schemas/AuthType'
oAuthProviders:
type: array
items:
$ref: '#/components/schemas/OAuthProvider'

OAuthProvider:
type: object
properties:
clientName:
type: string
authorizationUri:
type: string

AuthType:
type: string
enum:
- DISABLED
- OAUTH2
- LOGIN_FORM
- LDAP

Cluster:
type: object
properties:
Expand Down
Loading