Skip to content

Commit

Permalink
feat(revoke-share-code): support revoke shared login code (#1728)
Browse files Browse the repository at this point in the history
  • Loading branch information
baurine authored Aug 30, 2024
1 parent 6a0d342 commit b6a69ae
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3873,6 +3873,39 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati
options: localVarRequestOptions,
};
},
/**
*
* @summary Reset encryption key to revoke all authorized codes
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
userRevokeSession: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/user/share/revoke`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}

const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;

// authentication JwtAuth required
await setApiKeyToObject(localVarHeaderParameter, "Authorization", configuration)



setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};

return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @summary Create an impersonation
Expand Down Expand Up @@ -5217,6 +5250,16 @@ export const DefaultApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.userLogin(message, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @summary Reset encryption key to revoke all authorized codes
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async userRevokeSession(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.userRevokeSession(options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @summary Create an impersonation
Expand Down Expand Up @@ -6274,6 +6317,15 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa
userLogin(message: UserAuthenticateForm, options?: any): AxiosPromise<UserTokenResponse> {
return localVarFp.userLogin(message, options).then((request) => request(axios, basePath));
},
/**
*
* @summary Reset encryption key to revoke all authorized codes
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
userRevokeSession(options?: any): AxiosPromise<void> {
return localVarFp.userRevokeSession(options).then((request) => request(axios, basePath));
},
/**
*
* @summary Create an impersonation
Expand Down Expand Up @@ -8716,6 +8768,17 @@ export class DefaultApi extends BaseAPI {
return DefaultApiFp(this.configuration).userLogin(requestParameters.message, options).then((request) => request(this.axios, this.basePath));
}

/**
*
* @summary Reset encryption key to revoke all authorized codes
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof DefaultApi
*/
public userRevokeSession(options?: AxiosRequestConfig) {
return DefaultApiFp(this.configuration).userRevokeSession(options).then((request) => request(this.axios, this.basePath));
}

/**
*
* @summary Create an impersonation
Expand Down
16 changes: 16 additions & 0 deletions ui/packages/tidb-dashboard-client/swagger/spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -3631,6 +3631,22 @@
}
}
},
"/user/share/revoke": {
"post": {
"security": [
{
"JwtAuth": []
}
],
"summary": "Reset encryption key to revoke all authorized codes",
"operationId": "userRevokeSession",
"responses": {
"200": {
"description": ""
}
}
}
},
"/user/sign_out_info": {
"get": {
"security": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ class DataSource implements IUserProfileDataSource {
userShareSession(request: CodeShareRequest, options?: ReqConfig) {
return client.getInstance().userShareSession({ request }, options)
}

userRevokeSession(options?: ReqConfig) {
return client.getInstance().userRevokeSession(options)
}

metricsGetPromAddress(options?: ReqConfig) {
return client.getInstance().metricsGetPromAddress(options)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ class DataSource implements IUserProfileDataSource {
userShareSession(request: CodeShareRequest, options?: ReqConfig) {
return client.getInstance().userShareSession({ request }, options)
}

userRevokeSession(options?: ReqConfig) {
return client.getInstance().userRevokeSession(options)
}

metricsGetPromAddress(options?: ReqConfig) {
return client.getInstance().metricsGetPromAddress(options)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import {
CopyOutlined,
LogoutOutlined,
QuestionCircleOutlined,
RollbackOutlined,
ShareAltOutlined
} from '@ant-design/icons'
import {
Alert,
Button,
Divider,
Form,
message,
Modal,
Select,
Space,
Expand Down Expand Up @@ -40,6 +42,46 @@ const SHARE_SESSION_EXPIRY_HOURS = [
24 * 30
]

function RevokeSessionButton() {
const whoAmI = store.useState((s) => s.whoAmI)
const { t } = useTranslation()
const ctx = useContext(UserProfileContext)

function showRevokeConfirm() {
Modal.confirm({
title: t('user_profile.revoke_modal.title'),
content: t('user_profile.revoke_modal.content'),
okText: t('user_profile.revoke_modal.ok'),
cancelText: t('user_profile.revoke_modal.cancel'),
onOk() {
ctx?.ds.userRevokeSession().then(() => {
message.success(t('user_profile.revoke_modal.success_message'))
})
}
})
}

let button = (
<Button
onClick={showRevokeConfirm}
disabled={!whoAmI || !whoAmI.is_shareable}
>
<RollbackOutlined /> {t('user_profile.session.revoke')}
{Boolean(whoAmI && !whoAmI.is_shareable) && <QuestionCircleOutlined />}
</Button>
)

if (whoAmI && !whoAmI.is_shareable) {
button = (
<Tooltip title={t('user_profile.session.revoke_unavailable_tooltip')}>
{button}
</Tooltip>
)
}

return <>{button}</>
}

function ShareSessionButton() {
const ctx = useContext(UserProfileContext)

Expand Down Expand Up @@ -143,11 +185,6 @@ function ShareSessionButton() {
width={600}
>
<ReactMarkdown>{t('user_profile.share_session.text')}</ReactMarkdown>
<Alert
message={t('user_profile.share_session.warning')}
type="warning"
showIcon
/>
<Divider />
<Form
layout="vertical"
Expand Down Expand Up @@ -216,6 +253,8 @@ export function SessionForm() {
return (
<Space>
<ShareSessionButton />
{/* only available for v8.4.0+, v6.5.11+ */}
<RevokeSessionButton />
<Button danger onClick={handleLogout}>
<LogoutOutlined /> {t('user_profile.session.sign_out')}
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export interface IUserProfileDataSource {
options?: ReqConfig
): AxiosPromise<CodeShareResponse>

userRevokeSession(options?: ReqConfig): AxiosPromise<void>

metricsGetPromAddress(
options?: ReqConfig
): AxiosPromise<MetricsGetPromAddressConfigResponse>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,20 @@ user_profile:
sign_out: Sign Out
share: Share Current Session
share_unavailable_tooltip: Current session is not allowed to be shared
revoke: Revoke Authorization Codes
revoke_unavailable_tooltip: You have no permission to revoke the authorization codes
share_session:
text: >
You can invite others to access this {{distro.tidb}} Dashboard by sharing your
current session via an **Authorization Code**:
- The Authorization Code can be used multiple times.
- The shared session has the same privilege as your current session.
- The shared session will be invalidated after the expiry time you specified.
- The shared session has the same privilege as your current session.
warning: >
Warning: Shared session will remain valid and cannot be revoked until it is expired.
Keep the Authorization Code safe!
- The shared session can be revoked in advance by administrator.
form:
expire: Expire in
read_only: Share as read-only privilege
Expand All @@ -72,6 +73,12 @@ user_profile:
title: Authorization Code Generated
copy: Copy
copied: Copied
revoke_modal:
title: Are you sure you want to revoke all authorization codes?
content: After revoking, all authorization codes that are authorized before can't be used to login again, and this action can't undo.
ok: Revoke
cancel: Cancel
success_message: Revoke authorization codes successfully!
version:
title: Version Information
internal_version: '{{distro.tidb}} Dashboard Internal Version'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,19 @@ user_profile:
sign_out: 登出
share: 分享当前会话
share_unavailable_tooltip: 当前会话被禁止分享
revoke: 撤消授权码
revoke_unavailable_tooltip: 你没有权限撤消授权码
share_session:
text: >
您可以生成一个**授权码**来将您当前的会话分享给其他人邀请他们使用该 {{distro.tidb}} Dashboard:
您可以生成一个**授权码**来将您当前的会话分享给其他人邀请他们使用该 {{distro.tidb}} Dashboard:
- 授权码可以被重复使用。
- 分享的会话和您当前会话具有相同权限。
- 分享的会话将在您指定的有效时间后过期。
- 分享的会话和您当前会话具有相同权限。
warning: >
警告:已分享的会话无法被提前注销,将保持有效直到有效时间过期,因此请妥善保管授权码。
- 分享的会话可以被管理员提前撤消。
form:
expire: 有效时间
read_only: 以只读权限分享
Expand All @@ -70,6 +72,12 @@ user_profile:
title: 授权码已生成
copy: 复制
copied: 已复制
revoke_modal:
title: 你确定你要撤消所有的授权码吗?
content: 撤消之后,所有之前授权的授权码都不能再用于登录,而且这个操作不能回滚。
ok: 撤消
cancel: 取消
success_message: 撤消授权码成功!
version:
title: 版本信息
internal_version: '{{distro.tidb}} Dashboard 内部版本号'
Expand Down

0 comments on commit b6a69ae

Please sign in to comment.