From 25dea603eef99c7b7ca25eb1d2ba8104a2816f2a Mon Sep 17 00:00:00 2001 From: Roman Zabaluev Date: Fri, 25 Oct 2024 22:10:09 +0300 Subject: [PATCH 01/15] Misc --- .../{AuthController.java => AuthenticationController.java} | 4 ++-- .../{AccessController.java => AuthorizationController.java} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename api/src/main/java/io/kafbat/ui/controller/{AuthController.java => AuthenticationController.java} (97%) rename api/src/main/java/io/kafbat/ui/controller/{AccessController.java => AuthorizationController.java} (97%) diff --git a/api/src/main/java/io/kafbat/ui/controller/AuthController.java b/api/src/main/java/io/kafbat/ui/controller/AuthenticationController.java similarity index 97% rename from api/src/main/java/io/kafbat/ui/controller/AuthController.java rename to api/src/main/java/io/kafbat/ui/controller/AuthenticationController.java index e4532dda3..b50c64546 100644 --- a/api/src/main/java/io/kafbat/ui/controller/AuthController.java +++ b/api/src/main/java/io/kafbat/ui/controller/AuthenticationController.java @@ -13,13 +13,13 @@ @RestController @RequiredArgsConstructor @Slf4j -public class AuthController { +public class AuthenticationController { @GetMapping(value = "/auth", produces = {"text/html"}) public Mono getAuth(ServerWebExchange exchange) { Mono token = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty()); return token - .map(AuthController::csrfToken) + .map(AuthenticationController::csrfToken) .defaultIfEmpty("") .map(csrfTokenHtmlInput -> createPage(exchange, csrfTokenHtmlInput)); } diff --git a/api/src/main/java/io/kafbat/ui/controller/AccessController.java b/api/src/main/java/io/kafbat/ui/controller/AuthorizationController.java similarity index 97% rename from api/src/main/java/io/kafbat/ui/controller/AccessController.java rename to api/src/main/java/io/kafbat/ui/controller/AuthorizationController.java index 5833f2e3c..ea1ef7739 100644 --- a/api/src/main/java/io/kafbat/ui/controller/AccessController.java +++ b/api/src/main/java/io/kafbat/ui/controller/AuthorizationController.java @@ -26,7 +26,7 @@ @RestController @RequiredArgsConstructor @Slf4j -public class AccessController implements AuthorizationApi { +public class AuthorizationController implements AuthorizationApi { private final AccessControlService accessControlService; From 1e668f8910805623ef700c8ca52de8e215d7a504 Mon Sep 17 00:00:00 2001 From: Roman Zabaluev Date: Sat, 26 Oct 2024 01:07:44 +0300 Subject: [PATCH 02/15] Impl authentication page backend --- .../auth/AbstractAuthSecurityConfig.java | 3 +- .../ApplicationConfigController.java | 10 +++- .../ui/service/ApplicationInfoService.java | 47 ++++++++++++++++++- .../main/resources/swagger/kafbat-ui-api.yaml | 42 ++++++++++++++++- 4 files changed, 98 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/io/kafbat/ui/config/auth/AbstractAuthSecurityConfig.java b/api/src/main/java/io/kafbat/ui/config/auth/AbstractAuthSecurityConfig.java index f23a0dd2a..dc58a7299 100644 --- a/api/src/main/java/io/kafbat/ui/config/auth/AbstractAuthSecurityConfig.java +++ b/api/src/main/java/io/kafbat/ui/config/auth/AbstractAuthSecurityConfig.java @@ -18,7 +18,8 @@ protected AbstractAuthSecurityConfig() { "/login", "/logout", "/oauth2/**", - "/static/**" + "/static/**", + "/api/config/authentication" }; } diff --git a/api/src/main/java/io/kafbat/ui/controller/ApplicationConfigController.java b/api/src/main/java/io/kafbat/ui/controller/ApplicationConfigController.java index 5d5d4ed98..e8d763545 100644 --- a/api/src/main/java/io/kafbat/ui/controller/ApplicationConfigController.java +++ b/api/src/main/java/io/kafbat/ui/controller/ApplicationConfigController.java @@ -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; @@ -66,6 +67,13 @@ public Mono> getApplicationInfo(ServerWebExch return Mono.just(applicationInfoService.getApplicationInfo()).map(ResponseEntity::ok); } + @Override + public Mono> getAuthenticationSettings( + ServerWebExchange exchange) { + return Mono.just(applicationInfoService.getAuthenticationProperties()) + .map(ResponseEntity::ok); + } + @Override public Mono> getCurrentConfig(ServerWebExchange exchange) { var context = AccessContext.builder() @@ -109,7 +117,7 @@ public Mono> uploadConfigRelatedFile(Flux 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)); } diff --git a/api/src/main/java/io/kafbat/ui/service/ApplicationInfoService.java b/api/src/main/java/io/kafbat/ui/service/ApplicationInfoService.java index 0a04ce9d6..7cc7e3e41 100644 --- a/api/src/main/java/io/kafbat/ui/service/ApplicationInfoService.java +++ b/api/src/main/java/io/kafbat/ui/service/ApplicationInfoService.java @@ -1,36 +1,49 @@ 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 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.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; -import reactor.core.publisher.Mono; @Service public class ApplicationInfoService { private final GithubReleaseInfo githubReleaseInfo = new 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) { + 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())); @@ -68,6 +81,38 @@ private List 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 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) (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() { diff --git a/contract/src/main/resources/swagger/kafbat-ui-api.yaml b/contract/src/main/resources/swagger/kafbat-ui-api.yaml index 19be2abaa..8312c6da5 100644 --- a/contract/src/main/resources/swagger/kafbat-ui-api.yaml +++ b/contract/src/main/resources/swagger/kafbat-ui-api.yaml @@ -2124,7 +2124,7 @@ paths: get: tags: - Authorization - summary: Get user authentication related info + summary: Get user authorization related info operationId: getUserAuthInfo responses: 200: @@ -2218,6 +2218,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: @@ -2328,6 +2342,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: From d91e8b9241294dd163dd5fa344a8b498d165b1e8 Mon Sep 17 00:00:00 2001 From: Roman Zabaluev Date: Sun, 17 Nov 2024 20:45:12 +0800 Subject: [PATCH 03/15] Add POST auth endpoint --- .../main/resources/swagger/kafbat-ui-api.yaml | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/contract/src/main/resources/swagger/kafbat-ui-api.yaml b/contract/src/main/resources/swagger/kafbat-ui-api.yaml index 486a27bdc..da2d1a765 100644 --- a/contract/src/main/resources/swagger/kafbat-ui-api.yaml +++ b/contract/src/main/resources/swagger/kafbat-ui-api.yaml @@ -31,7 +31,6 @@ paths: items: $ref: '#/components/schemas/Cluster' - /api/clusters/{clusterName}/cache: post: tags: @@ -54,7 +53,6 @@ paths: 404: description: Not found - /api/clusters/{clusterName}/brokers: get: tags: @@ -432,7 +430,6 @@ paths: 404: description: Not found - /api/clusters/{clusterName}/topics/{topicName}: get: tags: @@ -2220,7 +2217,6 @@ paths: schema: $ref: '#/components/schemas/ApplicationConfigValidation' - /api/config/relatedfiles: post: tags: @@ -2258,6 +2254,26 @@ paths: schema: $ref: '#/components/schemas/AppAuthenticationSettings' + /auth: + post: + summary: Authenticate + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + username: + type: string + password: + type: string + responses: + '200': + description: OK + '401': + description: Unauthorized + components: schemas: TopicSerdeSuggestion: From 3e61608835c2d514cb814c87adcd8f8ed5ef7436 Mon Sep 17 00:00:00 2001 From: Renat Kalimulin Date: Mon, 20 May 2024 16:07:17 +0300 Subject: [PATCH 04/15] resolves #72 --- frontend/public/serviceImage.png | Bin 0 -> 7361 bytes frontend/src/components/App.tsx | 83 ++++++++++-------- .../components/AuthPage/AuthPage.styled.tsx | 14 +++ frontend/src/components/AuthPage/AuthPage.tsx | 18 ++++ .../AuthPage/Footer/Footer.styled.tsx | 45 ++++++++++ .../src/components/AuthPage/Footer/Footer.tsx | 28 ++++++ .../AuthPage/Header/Header.styled.tsx | 33 +++++++ .../src/components/AuthPage/Header/Header.tsx | 81 +++++++++++++++++ .../components/AuthPage/Header/HeaderLogo.tsx | 29 ++++++ .../SignIn/BasicSignIn/BasicSignIn.styled.tsx | 37 ++++++++ .../SignIn/BasicSignIn/BasicSignIn.tsx | 52 +++++++++++ .../AuthCard/AuthCard.styled.tsx | 58 ++++++++++++ .../ServiceSignIn/AuthCard/AuthCard.tsx | 36 ++++++++ .../ServiceSignIn/ServiceSignIn.styled.tsx | 7 ++ .../SignIn/ServiceSignIn/ServiceSignIn.tsx | 18 ++++ .../AuthPage/SignIn/SignIn.styled.tsx | 18 ++++ .../src/components/AuthPage/SignIn/SignIn.tsx | 21 +++++ .../components/common/Icons/GoogleIcon.tsx | 32 +++++++ .../components/common/Icons/ServiceImage.tsx | 11 +++ frontend/src/theme/theme.ts | 66 ++++++++++++++ 20 files changed, 649 insertions(+), 38 deletions(-) create mode 100644 frontend/public/serviceImage.png create mode 100644 frontend/src/components/AuthPage/AuthPage.styled.tsx create mode 100644 frontend/src/components/AuthPage/AuthPage.tsx create mode 100644 frontend/src/components/AuthPage/Footer/Footer.styled.tsx create mode 100644 frontend/src/components/AuthPage/Footer/Footer.tsx create mode 100644 frontend/src/components/AuthPage/Header/Header.styled.tsx create mode 100644 frontend/src/components/AuthPage/Header/Header.tsx create mode 100644 frontend/src/components/AuthPage/Header/HeaderLogo.tsx create mode 100644 frontend/src/components/AuthPage/SignIn/BasicSignIn/BasicSignIn.styled.tsx create mode 100644 frontend/src/components/AuthPage/SignIn/BasicSignIn/BasicSignIn.tsx create mode 100644 frontend/src/components/AuthPage/SignIn/ServiceSignIn/AuthCard/AuthCard.styled.tsx create mode 100644 frontend/src/components/AuthPage/SignIn/ServiceSignIn/AuthCard/AuthCard.tsx create mode 100644 frontend/src/components/AuthPage/SignIn/ServiceSignIn/ServiceSignIn.styled.tsx create mode 100644 frontend/src/components/AuthPage/SignIn/ServiceSignIn/ServiceSignIn.tsx create mode 100644 frontend/src/components/AuthPage/SignIn/SignIn.styled.tsx create mode 100644 frontend/src/components/AuthPage/SignIn/SignIn.tsx create mode 100644 frontend/src/components/common/Icons/GoogleIcon.tsx create mode 100644 frontend/src/components/common/Icons/ServiceImage.tsx diff --git a/frontend/public/serviceImage.png b/frontend/public/serviceImage.png new file mode 100644 index 0000000000000000000000000000000000000000..8006b13f5c09b4cee6289790a38df545177b25dc GIT binary patch literal 7361 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 literal 0 HcmV?d00001 diff --git a/frontend/src/components/App.tsx b/frontend/src/components/App.tsx index 16dd1305d..108be9191 100644 --- a/frontend/src/components/App.tsx +++ b/frontend/src/components/App.tsx @@ -1,5 +1,5 @@ import React, { Suspense, useContext } from 'react'; -import { Routes, Route, Navigate } from 'react-router-dom'; +import { Routes, Route, Navigate, useLocation } from 'react-router-dom'; import { accessErrorPage, clusterPath, @@ -24,6 +24,7 @@ import { GlobalSettingsProvider } from './contexts/GlobalSettingsContext'; import { UserInfoRolesAccessProvider } from './contexts/UserInfoRolesAccessContext'; import PageContainer from './PageContainer/PageContainer'; +const AuthPage = React.lazy(() => import('components/AuthPage/AuthPage')); const Dashboard = React.lazy(() => import('components/Dashboard/Dashboard')); const ClusterPage = React.lazy( () => import('components/ClusterPage/ClusterPage') @@ -49,51 +50,57 @@ const queryClient = new QueryClient({ }); const App: React.FC = () => { const { isDarkMode } = useContext(ThemeModeContext); + const location = useLocation(); + const isAuthPage = location.pathname === '/auth'; return ( }> - - - - - - - {['/', '/ui', '/ui/clusters'].map((path) => ( + {isAuthPage ? ( + + ) : ( + + + + + + + {['/', '/ui', '/ui/clusters'].map((path) => ( + } + /> + ))} } + path={getNonExactPath(clusterNewConfigPath)} + element={} /> - ))} - } - /> - } - /> - - } - /> - } /> - } - /> - - - - - - - + } + /> + + } + /> + } /> + } + /> + + + + + + + + )} diff --git a/frontend/src/components/AuthPage/AuthPage.styled.tsx b/frontend/src/components/AuthPage/AuthPage.styled.tsx new file mode 100644 index 000000000..16f86f714 --- /dev/null +++ b/frontend/src/components/AuthPage/AuthPage.styled.tsx @@ -0,0 +1,14 @@ +import styled, { css } from 'styled-components'; + +export const AuthPageStyled = styled.div( + ({ theme }) => css` + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + min-height: 100vh; + background-color: ${theme.auth_page.backgroundColor}; + font-family: ${theme.auth_page.fontFamily}; + overflow-x: hidden; + ` +); diff --git a/frontend/src/components/AuthPage/AuthPage.tsx b/frontend/src/components/AuthPage/AuthPage.tsx new file mode 100644 index 000000000..9d40e6274 --- /dev/null +++ b/frontend/src/components/AuthPage/AuthPage.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +import Footer from './Footer/Footer'; +import Header from './Header/Header'; +import SignIn from './SignIn/SignIn'; +import * as S from './AuthPage.styled'; + +function AuthPage() { + return ( + +
+ +