Skip to content

Commit b009648

Browse files
committed
feat: add database to authentication process
1 parent 249011d commit b009648

File tree

8 files changed

+95
-17
lines changed

8 files changed

+95
-17
lines changed

src/containers/App/Content.tsx

+19-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {connect} from 'react-redux';
44
import type {RedirectProps} from 'react-router-dom';
55
import {Redirect, Route, Switch} from 'react-router-dom';
66

7+
import {AccessDenied} from '../../components/Errors/403';
78
import {PageError} from '../../components/Errors/PageError/PageError';
89
import {LoaderWrapper} from '../../components/LoaderWrapper/LoaderWrapper';
910
import {useSlots} from '../../components/slots';
@@ -12,7 +13,11 @@ import type {SlotComponent} from '../../components/slots/types';
1213
import routes from '../../routes';
1314
import type {RootState} from '../../store';
1415
import {authenticationApi} from '../../store/reducers/authentication/authentication';
15-
import {useCapabilitiesLoaded, useCapabilitiesQuery} from '../../store/reducers/capabilities/hooks';
16+
import {
17+
useAccessTotallyRestricted,
18+
useCapabilitiesLoaded,
19+
useCapabilitiesQuery,
20+
} from '../../store/reducers/capabilities/hooks';
1621
import {nodesListApi} from '../../store/reducers/nodesList';
1722
import {cn} from '../../utils/cn';
1823
import {useDatabaseFromQuery} from '../../utils/hooks/useDatabaseFromQuery';
@@ -177,10 +182,14 @@ export function Content(props: ContentProps) {
177182

178183
function DataWrapper({children}: {children: React.ReactNode}) {
179184
return (
180-
<GetUser>
181-
<GetNodesList />
182-
<GetCapabilities>{children}</GetCapabilities>
183-
</GetUser>
185+
// capabilities is going to work without authentication, but not all running systems are supporting this yet
186+
<GetCapabilities>
187+
<GetUser>
188+
<GetNodesList />
189+
{/* this GetCapabilities will be removed */}
190+
<GetCapabilities>{children}</GetCapabilities>
191+
</GetUser>
192+
</GetCapabilities>
184193
);
185194
}
186195

@@ -219,6 +228,11 @@ interface ContentWrapperProps {
219228

220229
function ContentWrapper(props: ContentWrapperProps) {
221230
const {singleClusterMode, isAuthenticated} = props;
231+
const isForbidded = useAccessTotallyRestricted();
232+
233+
if (isForbidded) {
234+
return <AccessDenied />;
235+
}
222236

223237
return (
224238
<Switch>

src/containers/AsideNavigation/YdbInternalUser/YdbInternalUser.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {useHistory} from 'react-router-dom';
55
import routes, {createHref} from '../../../routes';
66
import {authenticationApi} from '../../../store/reducers/authentication/authentication';
77
import {cn} from '../../../utils/cn';
8+
import {useDatabaseFromQuery} from '../../../utils/hooks/useDatabaseFromQuery';
89
import i18n from '../i18n';
910

1011
import './YdbInternalUser.scss';
@@ -13,11 +14,15 @@ const b = cn('kv-ydb-internal-user');
1314

1415
export function YdbInternalUser({login}: {login?: string}) {
1516
const [logout] = authenticationApi.useLogoutMutation();
17+
const database = useDatabaseFromQuery();
1618

1719
const history = useHistory();
1820
const handleLoginClick = () => {
1921
history.push(
20-
createHref(routes.auth, undefined, {returnUrl: encodeURIComponent(location.href)}),
22+
createHref(routes.auth, undefined, {
23+
returnUrl: encodeURIComponent(location.href),
24+
database,
25+
}),
2126
);
2227
};
2328

src/containers/Authentication/Authentication.tsx

+27-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import {useHistory, useLocation} from 'react-router-dom';
66

77
import {parseQuery} from '../../routes';
88
import {authenticationApi} from '../../store/reducers/authentication/authentication';
9+
import {useLoginWithDatabase} from '../../store/reducers/capabilities/hooks';
910
import {cn} from '../../utils/cn';
1011

11-
import {isPasswordError, isUserError} from './utils';
12+
import {isDatabaseError, isPasswordError, isUserError} from './utils';
1213

1314
import ydbLogoIcon from '../../assets/icons/ydb.svg';
1415

@@ -24,28 +25,36 @@ function Authentication({closable = false}: AuthenticationProps) {
2425
const history = useHistory();
2526
const location = useLocation();
2627

28+
const needDatabase = useLoginWithDatabase();
29+
2730
const [authenticate, {isLoading}] = authenticationApi.useAuthenticateMutation(undefined);
2831

29-
const {returnUrl} = parseQuery(location);
32+
const {returnUrl, database: databaseFromQuery} = parseQuery(location);
3033

3134
const [login, setLogin] = React.useState('');
35+
const [database, setDatabase] = React.useState(databaseFromQuery?.toString() ?? '');
3236
const [password, setPass] = React.useState('');
3337
const [loginError, setLoginError] = React.useState('');
3438
const [passwordError, setPasswordError] = React.useState('');
39+
const [databaseError, setDatabaseError] = React.useState('');
3540
const [showPassword, setShowPassword] = React.useState(false);
3641

3742
const onLoginUpdate = (value: string) => {
3843
setLogin(value);
3944
setLoginError('');
4045
};
46+
const onDatabaseUpdate = (value: string) => {
47+
setDatabase(value);
48+
setDatabaseError('');
49+
};
4150

4251
const onPassUpdate = (value: string) => {
4352
setPass(value);
4453
setPasswordError('');
4554
};
4655

4756
const onLoginClick = () => {
48-
authenticate({user: login, password})
57+
authenticate({user: login, password, database})
4958
.unwrap()
5059
.then(() => {
5160
if (returnUrl) {
@@ -66,6 +75,9 @@ function Authentication({closable = false}: AuthenticationProps) {
6675
if (isPasswordError(error)) {
6776
setPasswordError(error.data.error);
6877
}
78+
if (isDatabaseError(error)) {
79+
setDatabaseError(error.data.error);
80+
}
6981
});
7082
};
7183

@@ -125,6 +137,18 @@ function Authentication({closable = false}: AuthenticationProps) {
125137
<Icon data={showPassword ? EyeSlash : Eye} size={16} />
126138
</Button>
127139
</div>
140+
{needDatabase && (
141+
<div className={b('field-wrapper')}>
142+
<TextInput
143+
value={database}
144+
onUpdate={onDatabaseUpdate}
145+
placeholder={'Database'}
146+
error={databaseError}
147+
onKeyDown={onEnterClick}
148+
size="l"
149+
/>
150+
</div>
151+
)}
128152
<Button
129153
view="action"
130154
onClick={onLoginClick}

src/services/api/auth.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {BaseYdbAPI} from './base';
22

33
export class AuthAPI extends BaseYdbAPI {
4-
authenticate(params: {user: string; password: string}) {
4+
authenticate(params: {user: string; password: string; database?: string}) {
55
return this.post(this.getPath('/login'), params, {});
66
}
77

src/store/reducers/authentication/authentication.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,10 @@ export const authenticationApi = api.injectEndpoints({
6565
providesTags: ['UserData'],
6666
}),
6767
authenticate: build.mutation({
68-
queryFn: async (params: {user: string; password: string}, {dispatch}) => {
68+
queryFn: async (
69+
params: {user: string; password: string; database?: string},
70+
{dispatch},
71+
) => {
6972
try {
7073
const data = await window.api.auth.authenticate(params);
7174
dispatch(setIsAuthenticated(true));

src/store/reducers/capabilities/capabilities.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {createSelector} from '@reduxjs/toolkit';
22

3-
import type {Capability} from '../../../types/api/capabilities';
3+
import type {Capability, SecuritySetting} from '../../../types/api/capabilities';
44
import type {AppDispatch, RootState} from '../../defaultStore';
55

66
import {api} from './../api';
@@ -38,8 +38,16 @@ export const selectCapabilityVersion = createSelector(
3838
(state: RootState) => state,
3939
(_state: RootState, capability: Capability) => capability,
4040
(_state: RootState, _capability: Capability, database?: string) => database,
41-
(state, capability, database) =>
42-
selectDatabaseCapabilities(state, database).data?.Capabilities?.[capability],
41+
(state, capability, database) => {
42+
return selectDatabaseCapabilities(state, database).data?.Capabilities?.[capability];
43+
},
44+
);
45+
export const selectSecuritySetting = createSelector(
46+
(state: RootState) => state,
47+
(_state: RootState, setting: SecuritySetting) => setting,
48+
(_state: RootState, _setting: SecuritySetting, database?: string) => database,
49+
(state, setting, database) =>
50+
selectDatabaseCapabilities(state, database).data?.Settings?.Security?.[setting],
4351
);
4452

4553
export async function queryCapability(
@@ -50,5 +58,5 @@ export async function queryCapability(
5058
const thunk = capabilitiesApi.util.getRunningQueryThunk('getClusterCapabilities', {database});
5159
await dispatch(thunk);
5260

53-
return selectCapabilityVersion(getState(), capability) || 0;
61+
return selectCapabilityVersion(getState(), capability, database) || 0;
5462
}

src/store/reducers/capabilities/hooks.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
import type {Capability} from '../../../types/api/capabilities';
1+
import type {Capability, SecuritySetting} from '../../../types/api/capabilities';
22
import {useTypedSelector} from '../../../utils/hooks';
33
import {useDatabaseFromQuery} from '../../../utils/hooks/useDatabaseFromQuery';
44

5-
import {capabilitiesApi, selectCapabilityVersion, selectDatabaseCapabilities} from './capabilities';
5+
import {
6+
capabilitiesApi,
7+
selectCapabilityVersion,
8+
selectDatabaseCapabilities,
9+
selectSecuritySetting,
10+
} from './capabilities';
611

712
export function useCapabilitiesQuery() {
813
const database = useDatabaseFromQuery();
@@ -68,3 +73,17 @@ export const useClusterDashboardAvailable = () => {
6873
export const useStreamingAvailable = () => {
6974
return useGetFeatureVersion('/viewer/query') >= 7;
7075
};
76+
77+
const useGetSecuritySetting = (feature: SecuritySetting) => {
78+
const database = useDatabaseFromQuery();
79+
80+
return useTypedSelector((state) => selectSecuritySetting(state, feature, database));
81+
};
82+
83+
export const useAccessTotallyRestricted = () => {
84+
return useGetSecuritySetting('UseLoginProvider') === false;
85+
};
86+
87+
export const useLoginWithDatabase = () => {
88+
return useGetSecuritySetting('DomainLoginOnly') === false;
89+
};

src/types/api/capabilities.ts

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
*/
44
export interface CapabilitiesResponse {
55
Capabilities: Record<Partial<Capability>, number>;
6+
Settings?: {
7+
Security?: Record<Partial<SecuritySetting>, boolean>;
8+
};
69
}
710

811
// Add feature name before using it
@@ -14,3 +17,5 @@ export type Capability =
1417
| '/viewer/feature_flags'
1518
| '/viewer/cluster'
1619
| '/viewer/nodes';
20+
21+
export type SecuritySetting = 'UseLoginProvider' | 'DomainLoginOnly';

0 commit comments

Comments
 (0)