This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
The following commit(s) were added to refs/heads/main by this push: new e5f5989a Fix #1044 e5f5989a is described below commit e5f5989a227c9f0630d46fba3ef3b83b25844db0 Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Wed Jan 3 18:51:15 2024 -0500 Fix #1044 --- .../apache/camel/karavan/api/UsersResource.java | 17 ++++-- .../apache/camel/karavan/service/AuthService.java | 3 +- .../karavan-app/src/main/webui/src/api/SsoApi.tsx | 13 +++++ .../src/main/webui/src/main/PageNavigation.tsx | 65 +++++++++++++++++++--- 4 files changed, 83 insertions(+), 15 deletions(-) diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/UsersResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/UsersResource.java index fbf6e61e..f63c1eb0 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/UsersResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/UsersResource.java @@ -17,6 +17,7 @@ package org.apache.camel.karavan.api; +import io.quarkus.oidc.UserInfo; import io.quarkus.security.identity.SecurityIdentity; import jakarta.inject.Inject; import jakarta.ws.rs.GET; @@ -34,21 +35,29 @@ public class UsersResource { @Inject SecurityIdentity securityIdentity; + @GET @Path("/me") @NoCache - public User me() { - return new User(securityIdentity); + public ProfileInfo me() { + return new ProfileInfo(securityIdentity); } - public static class User { + public static class ProfileInfo { private final String userName; + private final String displayName; private final Set<String> roles; - User(SecurityIdentity securityIdentity) { + ProfileInfo(SecurityIdentity securityIdentity) { + UserInfo userInfo = (UserInfo) securityIdentity.getAttributes().get("userinfo"); this.userName = securityIdentity.getPrincipal().getName(); this.roles = securityIdentity.getRoles(); + this.displayName = userInfo.getName(); + } + + public String getDisplayName() { + return displayName; } public String getUserName() { diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/AuthService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/AuthService.java index de6c3285..63abff24 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/AuthService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/AuthService.java @@ -20,7 +20,6 @@ import jakarta.enterprise.context.ApplicationScoped; import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.logging.Logger; -import java.net.MalformedURLException; import java.util.Map; @ApplicationScoped @@ -32,7 +31,7 @@ public class AuthService { return ConfigProvider.getConfig().getValue("karavan.auth", String.class); } - public Map<String, String> getSsoConfig() throws MalformedURLException { + public Map<String, String> getSsoConfig() { return Map.of( "url", ConfigProvider.getConfig().getValue("karavan.keycloak.url", String.class), "realm", ConfigProvider.getConfig().getValue("karavan.keycloak.realm", String.class), diff --git a/karavan-web/karavan-app/src/main/webui/src/api/SsoApi.tsx b/karavan-web/karavan-app/src/main/webui/src/api/SsoApi.tsx index 04ed5748..9ac7ed61 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/SsoApi.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/api/SsoApi.tsx @@ -35,4 +35,17 @@ export class SsoApi { }); }); } + + static logout(after: () => void) { + if (SsoApi.keycloak) { + SsoApi.keycloak.logout().then(value => { + console.log('SsoApi', 'User is now logout.'); + KaravanApi.isAuthorized = false; + window.location.reload(); + }).catch(reason => { + console.log('SsoApi', 'Error:', reason); + window.location.reload(); + }); + } + } } \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx b/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx index fc2adfd8..cbe27f56 100644 --- a/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx @@ -15,13 +15,21 @@ * limitations under the License. */ -import React, { useState} from 'react'; +import React, {useState} from 'react'; import { Button, Flex, FlexItem, Tooltip, - Divider, Popover, Badge, Spinner, Bullseye + Divider, + Popover, + Badge, + Spinner, + Bullseye, + DescriptionListDescription, + DescriptionListTerm, + DescriptionListGroup, + DescriptionList, Text, TextVariants, TextContent } from '@patternfly/react-core'; import {KaravanApi} from "../api/KaravanApi"; import '../designer/karavan.css'; @@ -36,6 +44,8 @@ import ServicesIcon from "@patternfly/react-icons/dist/js/icons/services-icon"; import {useAppConfigStore, useDevModeStore, useFileStore} from "../api/ProjectStore"; import {shallow} from "zustand/shallow"; import {useNavigate} from "react-router-dom"; +import LogoutIcon from "@patternfly/react-icons/dist/js/icons/door-open-icon"; +import {SsoApi} from "../api/SsoApi"; class MenuItem { pageId: string = ''; @@ -93,7 +103,7 @@ export function PageNavigation () { <Button id={page.pageId} icon={page.icon} variant={"plain"} className={pageId === page.pageId ? "nav-button-selected" : ""} onClick={event => { - setFile('none',undefined); + setFile('none', undefined); setPodName(undefined); setStatus("none"); setPageId(page.pageId); @@ -109,19 +119,56 @@ export function PageNavigation () { {KaravanApi.authType !== 'public' && <FlexItem alignSelf={{default: "alignSelfCenter"}}> <Popover + hasAutoWidth aria-label="Current user" position={"right-end"} hideOnOutsideClick={false} isVisible={showUser} shouldClose={(_event, tip) => setShowUser(false)} shouldOpen={(_event, tip) => setShowUser(true)} - headerContent={<div>{KaravanApi.me?.userName}</div>} + headerContent={ + <TextContent> + <Text component={TextVariants.h3}>Profile</Text> + </TextContent> + } bodyContent={ - <Flex direction={{default: "row"}}> - {KaravanApi.me?.roles && Array.isArray(KaravanApi.me?.roles) - && KaravanApi.me?.roles - .filter((r: string) => ['administrator', 'developer', 'viewer'].includes(r)) - .map((role: string) => <Badge id={role} isRead>{role}</Badge>)} + <DescriptionList isHorizontal> + <DescriptionListGroup> + <DescriptionListTerm>UserName</DescriptionListTerm> + <DescriptionListDescription>{KaravanApi.me?.userName}</DescriptionListDescription> + </DescriptionListGroup> + <DescriptionListGroup> + <DescriptionListTerm>Display Name</DescriptionListTerm> + <DescriptionListDescription>{KaravanApi.me?.displayName}</DescriptionListDescription> + </DescriptionListGroup> + <DescriptionListGroup> + <DescriptionListTerm>Roles</DescriptionListTerm> + <DescriptionListDescription> + <Flex direction={{default: "row"}} gap={{default: "gapXs"}}> + {KaravanApi.me?.roles && Array.isArray(KaravanApi.me?.roles) + && KaravanApi.me?.roles + .filter((r: string) => ['administrator', 'developer', 'viewer'].includes(r)) + .map((role: string) => <Badge key={role} id={role} + isRead>{role}</Badge>)} + </Flex> + </DescriptionListDescription> + </DescriptionListGroup> + </DescriptionList> + } + footerContent={ + <Flex justifyContent={{default: "justifyContentFlexEnd"}}> + <Button size="sm" + variant={"primary"} + icon={<LogoutIcon/>} + // component={'a'} + // href={KaravanApi.me.logoutUrl} + onClick={e => { + setShowUser(false); + SsoApi.logout(() => {}); + }} + > + Logout + </Button> </Flex> } >