Merge pull request #592 from ONLYOFFICE/bugfix/oauth2-scope
Bugfix/oauth2 scope
This commit is contained in:
commit
a77fd92d09
@ -242,7 +242,6 @@ const StyledProperties = styled.div`
|
|||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
|
||||||
.property-tag {
|
.property-tag {
|
||||||
background: red;
|
|
||||||
max-width: 195px;
|
max-width: 195px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: ${(props) => props.theme.infoPanel.details.tagBackground};
|
background: ${(props) => props.theme.infoPanel.details.tagBackground};
|
||||||
|
@ -56,6 +56,8 @@ const ScopesBlock = ({
|
|||||||
) => {
|
) => {
|
||||||
const isChecked = checkedScopes.includes(name);
|
const isChecked = checkedScopes.includes(name);
|
||||||
|
|
||||||
|
const isWrite = type === "write";
|
||||||
|
|
||||||
if (!isChecked) {
|
if (!isChecked) {
|
||||||
setFilteredScopes((val) => {
|
setFilteredScopes((val) => {
|
||||||
val[group].isChecked = true;
|
val[group].isChecked = true;
|
||||||
@ -88,6 +90,8 @@ const ScopesBlock = ({
|
|||||||
setCheckedScopes((val) => val.filter((v) => v !== name));
|
setCheckedScopes((val) => val.filter((v) => v !== name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isWrite) onAddScope("scopes", name.replace("write", "read"));
|
||||||
|
|
||||||
onAddScope("scopes", name);
|
onAddScope("scopes", name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import { TTranslation } from "@docspace/shared/types";
|
|||||||
import { ContextMenuModel } from "@docspace/shared/components/context-menu";
|
import { ContextMenuModel } from "@docspace/shared/components/context-menu";
|
||||||
|
|
||||||
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
|
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
|
||||||
|
import { Tag } from "@docspace/shared/components/tag";
|
||||||
|
|
||||||
const StyledContainer = styled.div<{
|
const StyledContainer = styled.div<{
|
||||||
showDescription: boolean;
|
showDescription: boolean;
|
||||||
@ -118,6 +119,23 @@ const StyledContainer = styled.div<{
|
|||||||
background: ${(props) => props.theme.oauth.infoDialog.separatorColor};
|
background: ${(props) => props.theme.oauth.infoDialog.separatorColor};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.property-tag_list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.property-tag {
|
||||||
|
max-width: 195px;
|
||||||
|
margin: 0;
|
||||||
|
background: ${(props) => props.theme.infoPanel.details.tagBackground};
|
||||||
|
p {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
StyledContainer.defaultProps = { theme: Base };
|
StyledContainer.defaultProps = { theme: Base };
|
||||||
@ -288,7 +306,6 @@ const InfoDialog = ({
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
className="block-header"
|
className="block-header"
|
||||||
fontSize="14px"
|
fontSize="14px"
|
||||||
@ -299,7 +316,6 @@ const InfoDialog = ({
|
|||||||
>
|
>
|
||||||
{t("Common:Website")}
|
{t("Common:Website")}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
fontSize="13px"
|
fontSize="13px"
|
||||||
lineHeight="15px"
|
lineHeight="15px"
|
||||||
@ -311,7 +327,21 @@ const InfoDialog = ({
|
|||||||
>
|
>
|
||||||
{client?.websiteUrl}
|
{client?.websiteUrl}
|
||||||
</Link>
|
</Link>
|
||||||
|
<Text
|
||||||
|
className="block-header"
|
||||||
|
fontSize="14px"
|
||||||
|
lineHeight="16px"
|
||||||
|
fontWeight="600"
|
||||||
|
noSelect
|
||||||
|
truncate
|
||||||
|
>
|
||||||
|
{t("Scopes")}
|
||||||
|
</Text>{" "}
|
||||||
|
<ScopeList
|
||||||
|
selectedScopes={client?.scopes || []}
|
||||||
|
scopes={scopeList || []}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
<Text
|
<Text
|
||||||
className="block-header"
|
className="block-header"
|
||||||
fontSize="14px"
|
fontSize="14px"
|
||||||
@ -322,11 +352,16 @@ const InfoDialog = ({
|
|||||||
>
|
>
|
||||||
{t("Access")}
|
{t("Access")}
|
||||||
</Text>
|
</Text>
|
||||||
<ScopeList
|
<div className="property-tag_list">
|
||||||
selectedScopes={client?.scopes || []}
|
{client?.scopes.map((scope) => (
|
||||||
scopes={scopeList || []}
|
<Tag
|
||||||
t={t}
|
key={scope}
|
||||||
/>
|
tag={scope}
|
||||||
|
className="property-tag"
|
||||||
|
label={scope}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
{isProfile && (
|
{isProfile && (
|
||||||
<>
|
<>
|
||||||
<Text
|
<Text
|
||||||
@ -351,7 +386,6 @@ const InfoDialog = ({
|
|||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
className="block-header"
|
className="block-header"
|
||||||
fontSize="14px"
|
fontSize="14px"
|
||||||
@ -362,7 +396,6 @@ const InfoDialog = ({
|
|||||||
>
|
>
|
||||||
{t("SupportAndLegalInfo")}
|
{t("SupportAndLegalInfo")}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
className="privacy-block"
|
className="privacy-block"
|
||||||
fontSize="13px"
|
fontSize="13px"
|
||||||
|
@ -27,6 +27,8 @@ const StyledImage = styled.img`
|
|||||||
height: 32px;
|
height: 32px;
|
||||||
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
||||||
|
object-fit: cover;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface NameCellProps {
|
interface NameCellProps {
|
||||||
|
@ -21,9 +21,11 @@ import {
|
|||||||
import { UserStore } from "@docspace/shared/store/UserStore";
|
import { UserStore } from "@docspace/shared/store/UserStore";
|
||||||
|
|
||||||
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
|
import { OAuthStoreProps } from "SRC_DIR/store/OAuthStore";
|
||||||
|
import { introspectDeveloperToken } from "@docspace/shared/api/oauth";
|
||||||
|
import { FieldContainer } from "@docspace/shared/components/field-container";
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
p {
|
.warning-text {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -42,11 +44,15 @@ const GenerateDeveloperTokenDialog = ({
|
|||||||
// const {} = useTranslation(["OAuth", "Common"]);
|
// const {} = useTranslation(["OAuth", "Common"]);
|
||||||
|
|
||||||
const [token, setToken] = React.useState("");
|
const [token, setToken] = React.useState("");
|
||||||
|
const [isValidToken, setIsValidToken] = React.useState(false);
|
||||||
|
const [tokenError, setTokenError] = React.useState("");
|
||||||
|
|
||||||
const [requestRunning, setRequestRunning] = React.useState(false);
|
const [requestRunning, setRequestRunning] = React.useState(false);
|
||||||
|
|
||||||
|
const timerRef = React.useRef<null | NodeJS.Timeout>(null);
|
||||||
|
|
||||||
const onRevoke = async () => {
|
const onRevoke = async () => {
|
||||||
if (!token || !client || requestRunning) return;
|
if (!token || !isValidToken || !client || requestRunning) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { clientId, clientSecret } = client;
|
const { clientId, clientSecret } = client;
|
||||||
@ -66,9 +72,42 @@ const GenerateDeveloperTokenDialog = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const onChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
|
|
||||||
|
if (timerRef.current) {
|
||||||
|
clearTimeout(timerRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
setIsValidToken(false);
|
||||||
|
setTokenError("");
|
||||||
|
setToken("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timerRef.current = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const data = await introspectDeveloperToken(value);
|
||||||
|
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
const { active, client_id: clientId } = data;
|
||||||
|
|
||||||
|
if (active && clientId === client?.clientId) {
|
||||||
|
setIsValidToken(true);
|
||||||
|
setTokenError("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsValidToken(false);
|
||||||
|
setTokenError("Invalid token");
|
||||||
|
} catch (err) {
|
||||||
|
setIsValidToken(false);
|
||||||
|
setTokenError("Invalid token");
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
|
||||||
setToken(value);
|
setToken(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -89,16 +128,23 @@ const GenerateDeveloperTokenDialog = ({
|
|||||||
<ModalDialog.Header>Revoke developer token</ModalDialog.Header>
|
<ModalDialog.Header>Revoke developer token</ModalDialog.Header>
|
||||||
<ModalDialog.Body>
|
<ModalDialog.Body>
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<Text>Warning text</Text>
|
<Text className="warning-text">Warning text</Text>
|
||||||
<TextInput
|
<FieldContainer
|
||||||
value={token}
|
hasError={!!tokenError}
|
||||||
scale
|
errorMessage={tokenError}
|
||||||
placeholder="Enter developer token"
|
removeMargin
|
||||||
type={InputType.text}
|
>
|
||||||
size={InputSize.base}
|
<TextInput
|
||||||
onChange={onChange}
|
value={token}
|
||||||
maxLength={10000}
|
scale
|
||||||
/>
|
placeholder="Enter developer token"
|
||||||
|
type={InputType.text}
|
||||||
|
size={InputSize.base}
|
||||||
|
onChange={onChange}
|
||||||
|
maxLength={10000}
|
||||||
|
hasError={!!tokenError}
|
||||||
|
/>
|
||||||
|
</FieldContainer>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</ModalDialog.Body>
|
</ModalDialog.Body>
|
||||||
<ModalDialog.Footer>
|
<ModalDialog.Footer>
|
||||||
@ -107,7 +153,7 @@ const GenerateDeveloperTokenDialog = ({
|
|||||||
primary
|
primary
|
||||||
scale
|
scale
|
||||||
onClick={onRevoke}
|
onClick={onRevoke}
|
||||||
isDisabled={!token}
|
isDisabled={!token || !isValidToken}
|
||||||
isLoading={requestRunning}
|
isLoading={requestRunning}
|
||||||
size={ButtonSize.small}
|
size={ButtonSize.small}
|
||||||
/>
|
/>
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
TConsentData,
|
TConsentData,
|
||||||
TConsentList,
|
TConsentList,
|
||||||
TGenerateDeveloperToken,
|
TGenerateDeveloperToken,
|
||||||
|
TIntrospectDeveloperToken,
|
||||||
} from "../../utils/oauth/types";
|
} from "../../utils/oauth/types";
|
||||||
|
|
||||||
export const getClient = async (clientId: string): Promise<IClientProps> => {
|
export const getClient = async (clientId: string): Promise<IClientProps> => {
|
||||||
@ -273,3 +274,14 @@ export const revokeDeveloperToken = (
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const introspectDeveloperToken = (token: string) => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append("token", token);
|
||||||
|
|
||||||
|
return request<TIntrospectDeveloperToken>(
|
||||||
|
{ method: "post", url: "/oauth2/introspect", data: params },
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -157,3 +157,19 @@ export type TGenerateDeveloperToken = {
|
|||||||
scope: string;
|
scope: string;
|
||||||
token_type: string;
|
token_type: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TIntrospectDeveloperToken = {
|
||||||
|
active: boolean;
|
||||||
|
sub: string;
|
||||||
|
aud: string[];
|
||||||
|
nbf: string;
|
||||||
|
scope: string;
|
||||||
|
iss: string;
|
||||||
|
exp: number;
|
||||||
|
iat: number;
|
||||||
|
jti: string;
|
||||||
|
tid: number;
|
||||||
|
cid: string;
|
||||||
|
client_id: string;
|
||||||
|
token_type: string;
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user