Skip to content

Commit f7a244f

Browse files
authored
Merge pull request #1555 from devtron-labs/feat/ad-user-group
feat: active directory support in SSO
2 parents 393436d + 0cc045a commit f7a244f

20 files changed

+880
-505
lines changed

src/components/apiTokens/authorization.utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export function getOptions(customDate) {
2424
export const PermissionType = [
2525
{ value: 'SPECIFIC', label: 'Specific permissions' },
2626
{ value: 'SUPERADMIN', label: 'Super admin permission' },
27-
]
27+
] as const
2828

2929
const millisecondsInDay = 86400000
3030

src/components/command/command.util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ function getGlobalConfigArguments(args, options): Promise<CommandSuggestionType>
496496
ref: null,
497497
data: {
498498
group: undefined,
499-
url: '/global-config/login-service',
499+
url: '/global-config/auth/login-service',
500500
isEOC: false,
501501
},
502502
},

src/components/common/ToastBody.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react'
22
import { toast } from 'react-toastify'
3-
import { TOAST_ACCESS_DENIED } from '../../config/constantMessaging'
3+
import { TOAST_ACCESS_DENIED } from '@devtron-labs/devtron-fe-common-lib'
44

55
export class ToastBody extends React.Component<{
66
title: string
@@ -62,4 +62,4 @@ export const toastAccessDenied = (title?: string, subtitle?: string) => {
6262
className: 'devtron-toast unauthorized',
6363
},
6464
)
65-
}
65+
}

src/components/globalConfigurations/GlobalConfiguration.tsx

Lines changed: 78 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import React, { lazy, useState, useEffect, Suspense, useContext } from 'react'
1+
import React, { lazy, useState, useEffect, Suspense, useContext, createContext } from 'react'
22
import { Route, NavLink, Router, Switch, Redirect } from 'react-router-dom'
33
import { useHistory, useLocation } from 'react-router'
44
import { URLS } from '../../config'
55
import { ErrorBoundary, importComponentFromFELibrary } from '../common'
6-
import { showError, Progressing, Toggle } from '@devtron-labs/devtron-fe-common-lib'
6+
import { showError, Progressing, Toggle, ConditionalWrap, TippyCustomized, TippyTheme } from '@devtron-labs/devtron-fe-common-lib'
77
import arrowTriangle from '../../assets/icons/ic-chevron-down.svg'
88
import { AddNotification } from '../notifications/AddNotification'
99
import { ReactComponent as FormError } from '../../assets/icons/ic-warning.svg'
@@ -24,6 +24,7 @@ import { ReactComponent as Dropdown } from '../../assets/icons/ic-chevron-down.s
2424
import { ModuleStatus } from '../v2/devtronStackManager/DevtronStackManager.type'
2525
import { getModuleInfo } from '../v2/devtronStackManager/DevtronStackManager.service'
2626
import { BodyType, ProtectedInputType } from './globalConfiguration.type'
27+
import { GlobalConfigurationProvider, useGlobalConfiguration } from './GlobalConfigurationProvider'
2728

2829
const HostURLConfiguration = lazy(() => import('../hostURL/HostURL'))
2930
const GitOpsConfiguration = lazy(() => import('../gitOps/GitOpsConfiguration'))
@@ -34,7 +35,6 @@ const ChartRepo = lazy(() => import('../chartRepo/ChartRepo'))
3435
const Notifier = lazy(() => import('../notifications/Notifications'))
3536
const Project = lazy(() => import('../project/ProjectList'))
3637
const UserGroup = lazy(() => import('../userGroups/UserGroup'))
37-
const SSOLogin = lazy(() => import('../login/SSOLogin'))
3838
const CustomChartList = lazy(() => import('../CustomChart/CustomChartList'))
3939
const ScopedVariables = lazy(() => import('../scopedVariables/ScopedVariables'))
4040
const CodeEditor = lazy(() => import('../CodeEditor/CodeEditor'))
@@ -124,22 +124,24 @@ export default function GlobalConfiguration(props) {
124124
<main className="global-configuration">
125125
<PageHeader headerName="Global configurations" />
126126
<Router history={useHistory()}>
127-
<section className="global-configuration__navigation">
128-
<NavItem serverMode={serverMode} />
129-
</section>
130-
<section className="global-configuration__component-wrapper">
131-
<Suspense fallback={<Progressing pageLoader />}>
132-
<ErrorBoundary>
133-
<Body
134-
isSuperAdmin={props.isSuperAdmin}
135-
getHostURLConfig={getHostURLConfig}
136-
checkList={checkList}
137-
serverMode={serverMode}
138-
handleChecklistUpdate={handleChecklistUpdate}
139-
/>
140-
</ErrorBoundary>
141-
</Suspense>
142-
</section>
127+
<GlobalConfigurationProvider>
128+
<section className="global-configuration__navigation">
129+
<NavItem serverMode={serverMode} />
130+
</section>
131+
<section className="global-configuration__component-wrapper">
132+
<Suspense fallback={<Progressing pageLoader />}>
133+
<ErrorBoundary>
134+
<Body
135+
isSuperAdmin={props.isSuperAdmin}
136+
getHostURLConfig={getHostURLConfig}
137+
checkList={checkList}
138+
serverMode={serverMode}
139+
handleChecklistUpdate={handleChecklistUpdate}
140+
/>
141+
</ErrorBoundary>
142+
</Suspense>
143+
</section>
144+
</GlobalConfigurationProvider>
143145
</Router>
144146
</main>
145147
)
@@ -153,6 +155,8 @@ function NavItem({ serverMode }) {
153155
const [collapsedState, setCollapsedState] = useState<Record<string, boolean>>({
154156
Authorization: location.pathname.startsWith('/global-config/auth') ? false : true,
155157
})
158+
const { tippyConfig, setTippyConfig } = useGlobalConfiguration()
159+
156160
let moduleStatusTimer = null
157161
const ConfigRequired = [
158162
{
@@ -192,12 +196,17 @@ function NavItem({ serverMode }) {
192196
component: CustomChartList,
193197
isAvailableInEA: false,
194198
},
195-
{ name: 'SSO Login Services', href: URLS.GLOBAL_CONFIG_LOGIN, component: SSOLogin, isAvailableInEA: true },
196199
{
197200
name: 'Authorization',
198201
href: `${URLS.GLOBAL_CONFIG_AUTH}/users`,
199202
preventDefaultKey: URLS.GLOBAL_CONFIG_AUTH,
200203
group: [
204+
{
205+
name: 'SSO Login Services',
206+
dataTestId: 'authorization-sso-login-link',
207+
href: `${URLS.GLOBAL_CONFIG_AUTH}/login-service`,
208+
isAvailableInEA: true,
209+
},
201210
{
202211
name: 'User Permissions',
203212
dataTestId: 'authorization-user-permissions-link',
@@ -260,28 +269,57 @@ function NavItem({ serverMode }) {
260269
}
261270

262271
const renderNavItem = (route, className = '', preventOnClickOp = false) => {
272+
const onTippyClose = () => {
273+
// Resetting the tippy state
274+
setTippyConfig({
275+
showTippy: false
276+
})
277+
}
278+
263279
return (
264-
<NavLink
265-
to={`${route.href}`}
266-
key={route.href}
267-
activeClassName="active-route"
268-
data-testid={route.dataTestId}
269-
className={`${
270-
route.name === 'API tokens' &&
271-
location.pathname.startsWith(`${URLS.GLOBAL_CONFIG_AUTH}/${Routes.API_TOKEN}`)
272-
? 'active-route'
273-
: ''
274-
}`}
275-
onClick={(e) => {
276-
if (!preventOnClickOp) {
277-
handleGroupCollapsedState(e, route)
278-
}
279-
}}
280+
// FIXME: Reuse the renderNavItem function for all nav item to extend the tippy support to all links
281+
<ConditionalWrap
282+
condition={tippyConfig.showTippy && tippyConfig.showOnRoute === route.href}
283+
wrap={(children) => (
284+
<TippyCustomized
285+
theme={TippyTheme.black}
286+
className="w-300 ml-2"
287+
placement="right"
288+
showCloseButton
289+
trigger="manual"
290+
interactive
291+
showOnCreate
292+
arrow
293+
animation="shift-toward-subtle"
294+
onClose={onTippyClose}
295+
{...tippyConfig}
296+
>
297+
{children}
298+
</TippyCustomized>
299+
)}
280300
>
281-
<div className={`flexbox flex-justify ${className || ''}`} data-testid={`${route.name}-page`}>
282-
<div>{route.name}</div>
283-
</div>
284-
</NavLink>
301+
<NavLink
302+
to={`${route.href}`}
303+
key={`${route.name}-${route.href}`}
304+
activeClassName="active-route"
305+
data-testid={route.dataTestId}
306+
className={`${
307+
route.name === 'API tokens' &&
308+
location.pathname.startsWith(`${URLS.GLOBAL_CONFIG_AUTH}/${Routes.API_TOKEN}`)
309+
? 'active-route'
310+
: ''
311+
}`}
312+
onClick={(e) => {
313+
if (!preventOnClickOp) {
314+
handleGroupCollapsedState(e, route)
315+
}
316+
}}
317+
>
318+
<div className={`flexbox flex-justify ${className || ''}`} data-testid={`${route.name}-page`}>
319+
<div>{route.name}</div>
320+
</div>
321+
</NavLink>
322+
</ConditionalWrap>
285323
)
286324
}
287325

@@ -529,13 +567,6 @@ function Body({ getHostURLConfig, checkList, serverMode, handleChecklistUpdate,
529567
<Route key={URLS.GLOBAL_CONFIG_CUSTOM_CHARTS} path={URLS.GLOBAL_CONFIG_CUSTOM_CHARTS}>
530568
<CustomChartList />
531569
</Route>,
532-
<Route
533-
key={URLS.GLOBAL_CONFIG_LOGIN}
534-
path={URLS.GLOBAL_CONFIG_LOGIN}
535-
render={(props) => {
536-
return <SSOLogin {...props} />
537-
}}
538-
/>,
539570
<Route
540571
key={URLS.GLOBAL_CONFIG_AUTH}
541572
path={URLS.GLOBAL_CONFIG_AUTH}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React, { createContext, useContext, useState } from 'react'
2+
import { GlobalConfiguration, TippyConfig } from './types'
3+
4+
const globalConfigurationContext = createContext<GlobalConfiguration>({} as GlobalConfiguration)
5+
6+
export const GlobalConfigurationProvider = ({ children }) => {
7+
const [tippyConfig, setTippyConfig] = useState<TippyConfig>({} as TippyConfig)
8+
9+
return (
10+
<globalConfigurationContext.Provider
11+
value={{
12+
tippyConfig,
13+
setTippyConfig,
14+
}}
15+
>
16+
{children}
17+
</globalConfigurationContext.Provider>
18+
)
19+
}
20+
21+
export const useGlobalConfiguration = () => useContext(globalConfigurationContext)
22+
23+
// For using the provider in class based components
24+
export const withGlobalConfiguration = (Component) => (props) => {
25+
const globalConfiguration = useGlobalConfiguration()
26+
return <Component {...props} globalConfiguration={globalConfiguration} />
27+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { TippyCustomizedProps } from "@devtron-labs/devtron-fe-common-lib"
2+
3+
export type TippyConfig =
4+
| (Omit<TippyCustomizedProps, 'theme' | 'children' | 'placement'> & {
5+
showTippy: true
6+
/**
7+
* The nav link route on which the Tippy should be shown
8+
*/
9+
showOnRoute: string
10+
})
11+
| {
12+
showTippy: false
13+
}
14+
15+
export interface GlobalConfiguration {
16+
tippyConfig: TippyConfig
17+
setTippyConfig: (tippyConfig: TippyConfig) => void
18+
}

0 commit comments

Comments
 (0)