diff --git a/.github/workflows/ci-build-deploy.yaml b/.github/workflows/ci-build-deploy.yaml index 8dcb6fc4f..ac8d44470 100644 --- a/.github/workflows/ci-build-deploy.yaml +++ b/.github/workflows/ci-build-deploy.yaml @@ -209,7 +209,9 @@ jobs: EXTERNAL_URL: value: 'https://api-services-portal-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}.apps.silver.devops.gov.bc.ca' NEXT_PUBLIC_API_ROOT: - value: 'https://api-services-portal-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}.apps.silver.devops.gov.bc.ca' + value: 'https://api-services-portal-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}.apps.silver.devops.gov.bc.ca' + NEXT_PUBLIC_GRAFANA_URL: + value: 'https://grafana-apps-gov-bc-ca.dev.api.gov.bc.ca' GWA_API_URL: value: 'https://gwa-api-gov-bc-ca.dev.api.gov.bc.ca' GWA_RES_SVR_CLIENT_ID: diff --git a/src/auth/auth-oauth2-proxy.js b/src/auth/auth-oauth2-proxy.js index cf75b9a31..b09f6c61b 100644 --- a/src/auth/auth-oauth2-proxy.js +++ b/src/auth/auth-oauth2-proxy.js @@ -266,7 +266,6 @@ class Oauth2ProxyAuthStrategy { const name = oauthUser['name']; const email = oauthUser['email']; const username = oauthUser['preferred_username']; - const namespace = oauthUser['namespace']; const groups = JSON.stringify(oauthUser['groups']); const _roles = []; const clientId = process.env.GWA_RES_SVR_CLIENT_ID; @@ -342,8 +341,8 @@ class Oauth2ProxyAuthStrategy { logger.debug('Temporary Credential NOT FOUND - CREATING AUTOMATICALLY'); const { errors } = await this.keystone.executeGraphQL({ context: this.keystone.createContext({ skipAccessControl: true }), - query: `mutation ($jti: String, $sub: String, $name: String, $email: String, $username: String, $namespace: String, $groups: String, $roles: String, $scopes: String, $userId: String) { - createTemporaryIdentity(data: {jti: $jti, sub: $sub, name: $name, username: $username, email: $email, isAdmin: false, namespace: $namespace, groups: $groups, roles: $roles, scopes: $scopes, userId: $userId }) { + query: `mutation ($jti: String, $sub: String, $name: String, $email: String, $username: String, $groups: String, $roles: String, $scopes: String, $userId: String) { + createTemporaryIdentity(data: {jti: $jti, sub: $sub, name: $name, username: $username, email: $email, isAdmin: false, groups: $groups, roles: $roles, scopes: $scopes, userId: $userId }) { id } }`, variables: { @@ -352,7 +351,6 @@ class Oauth2ProxyAuthStrategy { name, email, username, - namespace, groups, roles, scopes: '[]', diff --git a/src/nextapp/pages/manager/services/[id].tsx b/src/nextapp/pages/manager/services/[id].tsx index 740b3a292..a6a7272bb 100644 --- a/src/nextapp/pages/manager/services/[id].tsx +++ b/src/nextapp/pages/manager/services/[id].tsx @@ -2,18 +2,14 @@ import * as React from 'react'; import { Badge, Box, - Button, Container, Divider, Heading, - Icon, Skeleton, Table, Tbody, Td, Text, - Th, - Thead, Tr, Wrap, WrapItem, @@ -24,7 +20,6 @@ import PageHeader from '@/components/page-header'; import api, { useApi } from '@/shared/services/api'; import { dateRange } from '@/components/services-list/utils'; import { GET_GATEWAY_SERVICE } from '@/shared/queries/gateway-service-queries'; -import { FaExternalLinkSquareAlt } from 'react-icons/fa'; import EnvironmentBadge from '@/components/environment-badge'; import MetricGraph from '@/components/services-list/metric-graph'; import ServiceRoutes from '@/components/service-routes'; @@ -32,8 +27,8 @@ import { dehydrate } from 'react-query/hydration'; import { QueryClient } from 'react-query'; import { GetServerSideProps, InferGetServerSidePropsType } from 'next'; import { Query } from '@/shared/types/query.types'; - -import breadcrumbs from '@/components/ns-breadcrumb'; +import { useNamespaceBreadcrumbs } from '@/shared/hooks'; +import Head from 'next/head'; export const getServerSideProps: GetServerSideProps = async (context) => { const queryClient = new QueryClient(); @@ -61,6 +56,9 @@ export const getServerSideProps: GetServerSideProps = async (context) => { const ServicePage: React.FC< InferGetServerSidePropsType > = ({ id }) => { + const breadcrumb = useNamespaceBreadcrumbs([ + { href: '/manager/services', text: 'Services' }, + ]); const range = dateRange(); const { data } = useApi( ['gateway-service', id], @@ -72,82 +70,43 @@ const ServicePage: React.FC< }, { enabled: Boolean(id), suspense: false } ); - const breadcrumb = breadcrumbs([ - { href: '/manager/services', text: 'Services' }, - ]); const tags: string[] = !isEmpty(data?.GatewayService?.tags) ? (JSON.parse(data.GatewayService.tags) as string[]) : []; return ( - - } - > - View Full Metrics - - } - breadcrumb={breadcrumb} - title={ - - {data?.GatewayService.name} - - - } - /> - - - Metrics - - - - {id && data && ( - }> - + + {`API Program Services | Services | ${data?.GatewayService.name}`} + + + + {data?.GatewayService.name} + - - )} - - - - + + } + /> + - Stats + Metrics - + {id && data && ( }> - + - Routes + + Stats + + + + {id && data && ( + } + > + + + )} + - - - - - - - -
- -
-
- - - Details + + + Routes + + + + + + + + +
+ +
- - + - - Host - - {data?.GatewayService.host} - - Tags - - - - {tags.map((t) => ( - - {t} - - ))} - - + Details + + + + + + Host + + {data?.GatewayService.host} + + Tags + + + + {tags.map((t) => ( + + {t} + + ))} + + + -
-
+ + ); }; diff --git a/src/nextapp/pages/manager/services/index.tsx b/src/nextapp/pages/manager/services/index.tsx index f4bff0c7f..41e32dbb1 100644 --- a/src/nextapp/pages/manager/services/index.tsx +++ b/src/nextapp/pages/manager/services/index.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { Box, + Button, Container, Divider, Heading, @@ -11,33 +12,29 @@ import { Skeleton, } from '@chakra-ui/react'; import ClientRequest from '@/components/client-request'; +import { FaExternalLinkSquareAlt } from 'react-icons/fa'; import PageHeader from '@/components/page-header'; import ServicesList from '@/components/services-list'; import { useAuth /*, withAuth*/ } from '@/shared/services/auth'; import SearchInput from '@/components/search-input'; import { FaCaretSquareUp, FaFilter } from 'react-icons/fa'; import ServicesFilters from '@/components/services-list/services-filters'; +import { useNamespaceBreadcrumbs } from '@/shared/hooks'; +import Head from 'next/head'; +import { GetServerSideProps, InferGetServerSidePropsType } from 'next'; -import breadcrumbs from '@/components/ns-breadcrumb' - -// export const getServerSideProps = withAuth(async (context) => { -// const { user } = context; - -// if (!user) { -// return { -// redirect: { -// destination: '/unauthorized', -// permanent: false, -// }, -// }; -// } - -// return { -// props: {}, -// }; -// }); +export const getServerSideProps: GetServerSideProps = async () => { + return { + props: { + metricsUrl: process.env.NEXT_PUBLIC_GRAFANA_URL, + }, + }; +}; -const ServicesPage: React.FC = () => { +const ServicesPage: React.FC< + InferGetServerSidePropsType +> = ({ metricsUrl }) => { + const breadcrumb = useNamespaceBreadcrumbs(); const { user } = useAuth(); const [search, setSearch] = React.useState(''); const [showFilters, setShowFilters] = React.useState(false); @@ -46,53 +43,71 @@ const ServicesPage: React.FC = () => { }; return ( - - -

- List of services from the API Owner perspective. This should pull in - details from Prometheus and gwa-api Status. -

-
- - - - - 7 Day Metrics - - - - } + <> + + API Program Services | Services + + + - - - {showFilters && } - {user && ( - - ( - - ))} + href={metricsUrl} + rightIcon={} > - - - - )} - -
+ View Full Metrics + + } + title="Services" + breadcrumb={breadcrumb} + > +

+ List of services from the API Owner perspective. This should pull in + details from Prometheus and gwa-api Status. +

+ + + + + + 7 Day Metrics + + + + } + variant="primary" + onClick={onFilterClick} + /> + + + {showFilters && } + {user && ( + + ( + + ))} + > + + + + )} + + + ); }; diff --git a/src/nextapp/shared/config.ts b/src/nextapp/shared/config.ts index 9c1c70766..4fe0da8f8 100644 --- a/src/nextapp/shared/config.ts +++ b/src/nextapp/shared/config.ts @@ -1 +1,2 @@ export const apiHost = process.env.NEXT_PUBLIC_API_ROOT || ''; +export const grafanaUrl = process.env.NEXT_PUBLIC_GRAFANA_URL || ''; diff --git a/src/services/keycloak/templates/client-template-client-jwt.ts b/src/services/keycloak/templates/client-template-client-jwt.ts index f63f2953b..ebf722b44 100644 --- a/src/services/keycloak/templates/client-template-client-jwt.ts +++ b/src/services/keycloak/templates/client-template-client-jwt.ts @@ -1,54 +1,45 @@ - export const clientTemplateClientJwt = { - "clientId": "", - "name": "", - "description": "", - "surrogateAuthRequired": false, - "enabled": false, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-jwt", - "redirectUris": ["https://*"], - "webOrigins": ["*"], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": false, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": true, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "saml.assertion.signature": "false", - "saml.multivalued.roles": "false", - "saml.force.post.binding": "false", - "saml.encrypt": "false", - "saml.server.signature": "false", - "saml.server.signature.keyinfo.ext": "false", - "exclude.session.state.from.auth.response": "false", - "client_credentials.use_refresh_token": "false", - "saml_force_name_id_format": "false", - "saml.client.signature": "false", - "tls.client.certificate.bound.access.tokens": "false", - "saml.authnstatement": "false", - "display.on.consent.screen": "false", - "saml.onetimeuse.condition": "false", - "jwt.credential.certificate": "" + clientId: '', + name: '', + description: '', + surrogateAuthRequired: false, + enabled: false, + alwaysDisplayInConsole: false, + clientAuthenticatorType: 'client-jwt', + redirectUris: ['https://*'], + webOrigins: ['*'], + notBefore: 0, + bearerOnly: false, + consentRequired: false, + standardFlowEnabled: false, + implicitFlowEnabled: false, + directAccessGrantsEnabled: false, + serviceAccountsEnabled: true, + publicClient: false, + frontchannelLogout: false, + protocol: 'openid-connect', + attributes: { + 'saml.assertion.signature': 'false', + 'saml.multivalued.roles': 'false', + 'saml.force.post.binding': 'false', + 'saml.encrypt': 'false', + 'saml.server.signature': 'false', + 'saml.server.signature.keyinfo.ext': 'false', + 'exclude.session.state.from.auth.response': 'false', + 'client_credentials.use_refresh_token': 'false', + saml_force_name_id_format: 'false', + 'saml.client.signature': 'false', + 'tls.client.certificate.bound.access.tokens': 'false', + 'saml.authnstatement': 'false', + 'display.on.consent.screen': 'false', + 'saml.onetimeuse.condition': 'false', + 'jwt.credential.certificate': '', }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": -1, - "protocolMappers": [ - ] as string[], - "defaultClientScopes": [ - "web-origins", - "role_list", - "profile", - "roles" - ], - "optionalClientScopes": [ - "microprofile-jwt" - ], - "access": { "view": true, "configure": true, "manage": true } -} + authenticationFlowBindingOverrides: {}, + fullScopeAllowed: false, + nodeReRegistrationTimeout: -1, + protocolMappers: [] as string[], + defaultClientScopes: [] as string[], + optionalClientScopes: [] as string[], + access: { view: true, configure: true, manage: true }, +}; diff --git a/src/services/keycloak/templates/client-template-client-secret.ts b/src/services/keycloak/templates/client-template-client-secret.ts index 647e2408f..7dbd2731b 100644 --- a/src/services/keycloak/templates/client-template-client-secret.ts +++ b/src/services/keycloak/templates/client-template-client-secret.ts @@ -1,53 +1,44 @@ - export const clientTemplateClientSecret = { - "clientId": "", - "name": "", - "description": "", - "surrogateAuthRequired": false, - "enabled": false, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": ["https://*"], - "webOrigins": ["*"], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": false, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": true, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "saml.assertion.signature": "false", - "saml.multivalued.roles": "false", - "saml.force.post.binding": "false", - "saml.encrypt": "false", - "saml.server.signature": "false", - "saml.server.signature.keyinfo.ext": "false", - "exclude.session.state.from.auth.response": "false", - "client_credentials.use_refresh_token": "false", - "saml_force_name_id_format": "false", - "saml.client.signature": "false", - "tls.client.certificate.bound.access.tokens": "false", - "saml.authnstatement": "false", - "display.on.consent.screen": "false", - "saml.onetimeuse.condition": "false" + clientId: '', + name: '', + description: '', + surrogateAuthRequired: false, + enabled: false, + alwaysDisplayInConsole: false, + clientAuthenticatorType: 'client-secret', + redirectUris: ['https://*'], + webOrigins: ['*'], + notBefore: 0, + bearerOnly: false, + consentRequired: false, + standardFlowEnabled: false, + implicitFlowEnabled: false, + directAccessGrantsEnabled: false, + serviceAccountsEnabled: true, + publicClient: false, + frontchannelLogout: false, + protocol: 'openid-connect', + attributes: { + 'saml.assertion.signature': 'false', + 'saml.multivalued.roles': 'false', + 'saml.force.post.binding': 'false', + 'saml.encrypt': 'false', + 'saml.server.signature': 'false', + 'saml.server.signature.keyinfo.ext': 'false', + 'exclude.session.state.from.auth.response': 'false', + 'client_credentials.use_refresh_token': 'false', + saml_force_name_id_format: 'false', + 'saml.client.signature': 'false', + 'tls.client.certificate.bound.access.tokens': 'false', + 'saml.authnstatement': 'false', + 'display.on.consent.screen': 'false', + 'saml.onetimeuse.condition': 'false', }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": -1, - "protocolMappers": [ - ] as string[], - "defaultClientScopes": [ - "web-origins", - "role_list", - "profile", - "roles" - ], - "optionalClientScopes": [ - "microprofile-jwt" - ], - "access": { "view": true, "configure": true, "manage": true } -} + authenticationFlowBindingOverrides: {}, + fullScopeAllowed: false, + nodeReRegistrationTimeout: -1, + protocolMappers: [] as string[], + defaultClientScopes: [] as string[], + optionalClientScopes: [] as string[], + access: { view: true, configure: true, manage: true }, +};