mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-08 14:14:56 +03:00
feat(security token): security token manager implemented
security token manager flow implemented. token examples now can be add to store and components can listen to store change to get tokens submitted by the user
This commit is contained in:
parent
4b4f30f344
commit
cad8197a81
|
@ -38,8 +38,6 @@ info:
|
||||||
OAuth2 - an open protocol to allow secure authorization in a simple
|
OAuth2 - an open protocol to allow secure authorization in a simple
|
||||||
and standard method from web, mobile and desktop applications.
|
and standard method from web, mobile and desktop applications.
|
||||||
|
|
||||||
<SecurityDefinitions />
|
|
||||||
|
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
title: Swagger Petstore
|
title: Swagger Petstore
|
||||||
termsOfService: 'http://swagger.io/terms/'
|
termsOfService: 'http://swagger.io/terms/'
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { renderToString } from 'react-dom/server';
|
|
||||||
import * as React from 'react';
|
|
||||||
import { ServerStyleSheet } from 'styled-components';
|
|
||||||
// @ts-ignore
|
|
||||||
import { Redoc, createStore } from '../../';
|
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { renderToString } from 'react-dom/server';
|
||||||
|
import { ServerStyleSheet } from 'styled-components';
|
||||||
|
|
||||||
|
import { createStore, Redoc } from '../../';
|
||||||
|
|
||||||
const yaml = require('yaml-js');
|
const yaml = require('yaml-js');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
|
@ -19,7 +19,7 @@ const server = http.createServer(async (request, response) => {
|
||||||
fs.createReadStream('bundles/redoc.standalone.js', 'utf8').pipe(response);
|
fs.createReadStream('bundles/redoc.standalone.js', 'utf8').pipe(response);
|
||||||
} else if (request.url === '/') {
|
} else if (request.url === '/') {
|
||||||
const spec = yaml.load(readFileSync(resolve(__dirname, '../openapi.yaml')));
|
const spec = yaml.load(readFileSync(resolve(__dirname, '../openapi.yaml')));
|
||||||
let store = await createStore(spec, 'path/to/spec.yaml');
|
const store = await createStore(spec, 'path/to/spec.yaml');
|
||||||
|
|
||||||
const sheet = new ServerStyleSheet();
|
const sheet = new ServerStyleSheet();
|
||||||
|
|
||||||
|
|
|
@ -136,8 +136,6 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"@types/chai": "4.1.3",
|
|
||||||
"@types/tapable": "1.0.2",
|
|
||||||
"ajv": "^6.4.0",
|
"ajv": "^6.4.0",
|
||||||
"ajv-errors": "^1.0.0",
|
"ajv-errors": "^1.0.0",
|
||||||
"brace": "^0.11.1",
|
"brace": "^0.11.1",
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { TokenGroup } from '..';
|
||||||
|
|
||||||
|
import { DarkRightPanel, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
||||||
|
|
||||||
import { SecuritySchemesModel } from '../../services/models';
|
import { SecuritySchemesModel } from '../../services/models';
|
||||||
|
|
||||||
import { H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
|
||||||
import { OpenAPISecurityScheme } from '../../types';
|
import { OpenAPISecurityScheme } from '../../types';
|
||||||
import { titleize } from '../../utils/helpers';
|
import { titleize } from '../../utils/helpers';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
|
@ -18,11 +20,12 @@ const AUTH_TYPES = {
|
||||||
export interface OAuthFlowProps {
|
export interface OAuthFlowProps {
|
||||||
type: string;
|
type: string;
|
||||||
flow: OpenAPISecurityScheme['flows'][keyof OpenAPISecurityScheme['flows']];
|
flow: OpenAPISecurityScheme['flows'][keyof OpenAPISecurityScheme['flows']];
|
||||||
|
token?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
|
export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
|
||||||
render() {
|
render() {
|
||||||
const { type, flow } = this.props;
|
const { type, flow, token } = this.props;
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<th> {type} OAuth Flow </th>
|
<th> {type} OAuth Flow </th>
|
||||||
|
@ -56,6 +59,7 @@ export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
|
<td> {token} </td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -65,7 +69,31 @@ export interface SecurityDefsProps {
|
||||||
securitySchemes: SecuritySchemesModel;
|
securitySchemes: SecuritySchemesModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
|
export interface SecurityDefsState {
|
||||||
|
tokens: Dict<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class SecurityDefs extends React.PureComponent<SecurityDefsProps, SecurityDefsState> {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
tokens: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
mutateToken = (scheme, id) => {
|
||||||
|
return () => {
|
||||||
|
scheme.setToken(this.state.tokens[id]);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
setToken = id => {
|
||||||
|
return token => {
|
||||||
|
const tokens = this.state.tokens;
|
||||||
|
tokens[id] = token;
|
||||||
|
this.setState({tokens});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return this.props.securitySchemes.schemes.map(scheme => (
|
return this.props.securitySchemes.schemes.map(scheme => (
|
||||||
<Section id={scheme.sectionId} key={scheme.id}>
|
<Section id={scheme.sectionId} key={scheme.id}>
|
||||||
|
@ -82,22 +110,26 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
|
||||||
<tr>
|
<tr>
|
||||||
<th> Security scheme type: </th>
|
<th> Security scheme type: </th>
|
||||||
<td> {AUTH_TYPES[scheme.type] || scheme.type} </td>
|
<td> {AUTH_TYPES[scheme.type] || scheme.type} </td>
|
||||||
|
<td> Value </td>
|
||||||
</tr>
|
</tr>
|
||||||
{scheme.apiKey ? (
|
{scheme.apiKey ? (
|
||||||
<tr>
|
<tr>
|
||||||
<th> {titleize(scheme.apiKey.in || '')} parameter name:</th>
|
<th> {titleize(scheme.apiKey.in || '')} parameter name:</th>
|
||||||
<td> {scheme.apiKey.name} </td>
|
<td> {scheme.apiKey.name} </td>
|
||||||
|
<td> {scheme.token} </td>
|
||||||
</tr>
|
</tr>
|
||||||
) : scheme.http ? (
|
) : scheme.http ? (
|
||||||
[
|
[
|
||||||
<tr key="scheme">
|
<tr key="scheme">
|
||||||
<th> HTTP Authorization Scheme </th>
|
<th> HTTP Authorization Scheme </th>
|
||||||
<td> {scheme.http.scheme} </td>
|
<td> {scheme.http.scheme} </td>
|
||||||
|
<td> {scheme.token} </td>
|
||||||
</tr>,
|
</tr>,
|
||||||
scheme.http.scheme === 'bearer' && scheme.http.bearerFormat && (
|
scheme.http.scheme === 'bearer' && scheme.http.bearerFormat && (
|
||||||
<tr key="bearer">
|
<tr key="bearer">
|
||||||
<th> Bearer format </th>
|
<th> Bearer format </th>
|
||||||
<td> "{scheme.http.bearerFormat}" </td>
|
<td> "{scheme.http.bearerFormat}" </td>
|
||||||
|
<td> {scheme.token} </td>
|
||||||
</tr>
|
</tr>
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -109,16 +141,25 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
|
||||||
{scheme.openId.connectUrl}
|
{scheme.openId.connectUrl}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td> {scheme.token} </td>
|
||||||
</tr>
|
</tr>
|
||||||
) : scheme.flows ? (
|
) : scheme.flows ? (
|
||||||
Object.keys(scheme.flows).map(type => (
|
Object.keys(scheme.flows).map(type => (
|
||||||
<OAuthFlow key={type} type={type} flow={scheme.flows[type]} />
|
<OAuthFlow key={type} type={type} token={scheme.token} flow={scheme.flows[type]} />
|
||||||
))
|
))
|
||||||
) : null}
|
) : null}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</StyledMarkdownBlock>
|
</StyledMarkdownBlock>
|
||||||
</MiddlePanel>
|
</MiddlePanel>
|
||||||
|
<DarkRightPanel>
|
||||||
|
<TokenGroup
|
||||||
|
title={'Enter ' + scheme.id}
|
||||||
|
description={'You can add token here and store it to use in your request calls in this page.'}
|
||||||
|
onChange={this.setToken(scheme.sectionId)}
|
||||||
|
onSubmit={this.mutateToken(scheme, scheme.sectionId)}
|
||||||
|
/>
|
||||||
|
</DarkRightPanel>
|
||||||
</Row>
|
</Row>
|
||||||
</Section>
|
</Section>
|
||||||
));
|
));
|
||||||
|
|
84
src/components/TokenGroup/TokenGroup.tsx
Normal file
84
src/components/TokenGroup/TokenGroup.tsx
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import {Button, RightPanelHeader} from '../../common-elements';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
|
const SaveTokenButton = styled(Button)`
|
||||||
|
padding: 10px 30px;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
outline: none;
|
||||||
|
margin: 0
|
||||||
|
min-width: 60px;
|
||||||
|
max-width: 100px;
|
||||||
|
font-weight: bold;
|
||||||
|
flex: 1 1;
|
||||||
|
order: 2;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TokenTextField = styled.input`
|
||||||
|
padding: 10px 30px 10px 20px;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
background-color: ${props => props.theme.codeSample.backgroundColor};
|
||||||
|
color: ${props => props.theme.codeSample.textColor}
|
||||||
|
white-space: nowrap;
|
||||||
|
align-items: center;
|
||||||
|
border: none;
|
||||||
|
direction: ltr;
|
||||||
|
min-width: 300px;
|
||||||
|
flex: 4 1;
|
||||||
|
order: 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TokenGroupContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
align-content: flex-start;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Description = styled.p`
|
||||||
|
color: white;
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface TokenGroupProps {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
onSubmit: () => void;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TokenGroup extends React.PureComponent<TokenGroupProps> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.submit = this.submit.bind(this);
|
||||||
|
this.change = this.change.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
submit() {
|
||||||
|
this.props.onSubmit();
|
||||||
|
}
|
||||||
|
|
||||||
|
change(e) {
|
||||||
|
this.props.onChange(e.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RightPanelHeader>
|
||||||
|
{this.props.title}
|
||||||
|
</RightPanelHeader>
|
||||||
|
<TokenGroupContainer>
|
||||||
|
<TokenTextField onChange={this.change} />
|
||||||
|
<SaveTokenButton onClick={this.submit}>Save</SaveTokenButton>
|
||||||
|
</TokenGroupContainer>
|
||||||
|
<Description>
|
||||||
|
{this.props.description}
|
||||||
|
</Description>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,3 +30,4 @@ export * from './StickySidebar/StickyResponsiveSidebar';
|
||||||
export * from './SearchBox/SearchBox';
|
export * from './SearchBox/SearchBox';
|
||||||
export * from './SchemaDefinition/SchemaDefinition';
|
export * from './SchemaDefinition/SchemaDefinition';
|
||||||
export * from './SourceCode/SourceCode';
|
export * from './SourceCode/SourceCode';
|
||||||
|
export * from './TokenGroup/TokenGroup';
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import {observable} from 'mobx';
|
||||||
import { OpenAPISecurityScheme, Referenced } from '../../types';
|
import { OpenAPISecurityScheme, Referenced } from '../../types';
|
||||||
import { SECURITY_SCHEMES_SECTION_PREFIX } from '../../utils/openapi';
|
import { SECURITY_SCHEMES_SECTION_PREFIX } from '../../utils/openapi';
|
||||||
import { OpenAPIParser } from '../OpenAPIParser';
|
import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
|
@ -22,6 +23,9 @@ export class SecuritySchemeModel {
|
||||||
connectUrl: string;
|
connectUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@observable
|
||||||
|
token?: string = '';
|
||||||
|
|
||||||
constructor(parser: OpenAPIParser, id: string, scheme: Referenced<OpenAPISecurityScheme>) {
|
constructor(parser: OpenAPIParser, id: string, scheme: Referenced<OpenAPISecurityScheme>) {
|
||||||
const info = parser.deref(scheme);
|
const info = parser.deref(scheme);
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
@ -52,9 +56,14 @@ export class SecuritySchemeModel {
|
||||||
this.flows = info.flows;
|
this.flows = info.flows;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setToken(token: string) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SecuritySchemesModel {
|
export class SecuritySchemesModel {
|
||||||
|
@observable
|
||||||
schemes: SecuritySchemeModel[];
|
schemes: SecuritySchemeModel[];
|
||||||
|
|
||||||
constructor(parser: OpenAPIParser) {
|
constructor(parser: OpenAPIParser) {
|
||||||
|
|
|
@ -149,6 +149,7 @@ const defaultTheme: ThemeInterface = {
|
||||||
},
|
},
|
||||||
codeSample: {
|
codeSample: {
|
||||||
backgroundColor: ({ rightPanel }) => darken(0.1, rightPanel.backgroundColor),
|
backgroundColor: ({ rightPanel }) => darken(0.1, rightPanel.backgroundColor),
|
||||||
|
textColor: ({ rightPanel }) => rightPanel.textColor,
|
||||||
},
|
},
|
||||||
styledPre: {
|
styledPre: {
|
||||||
maxHeight: '500px',
|
maxHeight: '500px',
|
||||||
|
@ -324,6 +325,7 @@ export interface ResolvedThemeInterface {
|
||||||
};
|
};
|
||||||
codeSample: {
|
codeSample: {
|
||||||
backgroundColor: string;
|
backgroundColor: string;
|
||||||
|
textColor: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
extensionsHook?: (name: string, props: any) => string;
|
extensionsHook?: (name: string, props: any) => string;
|
||||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -923,10 +923,10 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.3.0"
|
"@babel/types" "^7.3.0"
|
||||||
|
|
||||||
"@types/chai@4.1.3":
|
"@types/chai@4.1.7":
|
||||||
version "4.1.3"
|
version "4.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.3.tgz#b8a74352977a23b604c01aa784f5b793443fb7dc"
|
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a"
|
||||||
integrity sha512-f5dXGzOJycyzSMdaXVhiBhauL4dYydXwVpavfQ1mVCaGjR56a9QfklXObUxlIY9bGTmCPHEEZ04I16BZ/8w5ww==
|
integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==
|
||||||
|
|
||||||
"@types/cheerio@*":
|
"@types/cheerio@*":
|
||||||
version "0.22.12"
|
version "0.22.12"
|
||||||
|
@ -1132,16 +1132,11 @@
|
||||||
"@types/react-native" "*"
|
"@types/react-native" "*"
|
||||||
csstype "^2.2.0"
|
csstype "^2.2.0"
|
||||||
|
|
||||||
"@types/tapable@*":
|
"@types/tapable@*", "@types/tapable@1.0.4":
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370"
|
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370"
|
||||||
integrity sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ==
|
integrity sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ==
|
||||||
|
|
||||||
"@types/tapable@1.0.2":
|
|
||||||
version "1.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.2.tgz#e13182e1b69871a422d7863e11a4a6f5b814a4bd"
|
|
||||||
integrity sha512-42zEJkBpNfMEAvWR5WlwtTH22oDzcMjFsL9gDGExwF8X8WvAiw7Vwop7hPw03QT8TKfec83LwbHj6SvpqM4ELQ==
|
|
||||||
|
|
||||||
"@types/uglify-js@*":
|
"@types/uglify-js@*":
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082"
|
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user