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:
Arian Rahimi 2020-01-19 12:52:06 +03:30
parent 4b4f30f344
commit cad8197a81
9 changed files with 153 additions and 25 deletions

View File

@ -38,8 +38,6 @@ info:
OAuth2 - an open protocol to allow secure authorization in a simple
and standard method from web, mobile and desktop applications.
<SecurityDefinitions />
version: 1.0.0
title: Swagger Petstore
termsOfService: 'http://swagger.io/terms/'

View File

@ -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 { 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 http = require('http');
@ -19,7 +19,7 @@ const server = http.createServer(async (request, response) => {
fs.createReadStream('bundles/redoc.standalone.js', 'utf8').pipe(response);
} else if (request.url === '/') {
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();

View File

@ -136,8 +136,6 @@
},
"dependencies": {
"classnames": "^2.2.6",
"@types/chai": "4.1.3",
"@types/tapable": "1.0.2",
"ajv": "^6.4.0",
"ajv-errors": "^1.0.0",
"brace": "^0.11.1",

View File

@ -1,8 +1,10 @@
import { observer } from 'mobx-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 { H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
import { OpenAPISecurityScheme } from '../../types';
import { titleize } from '../../utils/helpers';
import { Markdown } from '../Markdown/Markdown';
@ -18,11 +20,12 @@ const AUTH_TYPES = {
export interface OAuthFlowProps {
type: string;
flow: OpenAPISecurityScheme['flows'][keyof OpenAPISecurityScheme['flows']];
token?: string;
}
export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
render() {
const { type, flow } = this.props;
const { type, flow, token } = this.props;
return (
<tr>
<th> {type} OAuth Flow </th>
@ -56,6 +59,7 @@ export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
))}
</ul>
</td>
<td> {token} </td>
</tr>
);
}
@ -65,7 +69,31 @@ export interface SecurityDefsProps {
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() {
return this.props.securitySchemes.schemes.map(scheme => (
<Section id={scheme.sectionId} key={scheme.id}>
@ -82,22 +110,26 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
<tr>
<th> Security scheme type: </th>
<td> {AUTH_TYPES[scheme.type] || scheme.type} </td>
<td> Value </td>
</tr>
{scheme.apiKey ? (
<tr>
<th> {titleize(scheme.apiKey.in || '')} parameter name:</th>
<td> {scheme.apiKey.name} </td>
<td> {scheme.token} </td>
</tr>
) : scheme.http ? (
[
<tr key="scheme">
<th> HTTP Authorization Scheme </th>
<td> {scheme.http.scheme} </td>
<td> {scheme.token} </td>
</tr>,
scheme.http.scheme === 'bearer' && scheme.http.bearerFormat && (
<tr key="bearer">
<th> Bearer format </th>
<td> "{scheme.http.bearerFormat}" </td>
<td> {scheme.token} </td>
</tr>
),
]
@ -109,16 +141,25 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
{scheme.openId.connectUrl}
</a>
</td>
<td> {scheme.token} </td>
</tr>
) : scheme.flows ? (
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}
</tbody>
</table>
</StyledMarkdownBlock>
</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>
</Section>
));

View 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>
</>
);
}
}

View File

@ -30,3 +30,4 @@ export * from './StickySidebar/StickyResponsiveSidebar';
export * from './SearchBox/SearchBox';
export * from './SchemaDefinition/SchemaDefinition';
export * from './SourceCode/SourceCode';
export * from './TokenGroup/TokenGroup';

View File

@ -1,3 +1,4 @@
import {observable} from 'mobx';
import { OpenAPISecurityScheme, Referenced } from '../../types';
import { SECURITY_SCHEMES_SECTION_PREFIX } from '../../utils/openapi';
import { OpenAPIParser } from '../OpenAPIParser';
@ -22,6 +23,9 @@ export class SecuritySchemeModel {
connectUrl: string;
};
@observable
token?: string = '';
constructor(parser: OpenAPIParser, id: string, scheme: Referenced<OpenAPISecurityScheme>) {
const info = parser.deref(scheme);
this.id = id;
@ -52,9 +56,14 @@ export class SecuritySchemeModel {
this.flows = info.flows;
}
}
setToken(token: string) {
this.token = token;
}
}
export class SecuritySchemesModel {
@observable
schemes: SecuritySchemeModel[];
constructor(parser: OpenAPIParser) {

View File

@ -149,6 +149,7 @@ const defaultTheme: ThemeInterface = {
},
codeSample: {
backgroundColor: ({ rightPanel }) => darken(0.1, rightPanel.backgroundColor),
textColor: ({ rightPanel }) => rightPanel.textColor,
},
styledPre: {
maxHeight: '500px',
@ -324,6 +325,7 @@ export interface ResolvedThemeInterface {
};
codeSample: {
backgroundColor: string;
textColor: string;
};
extensionsHook?: (name: string, props: any) => string;

View File

@ -923,10 +923,10 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/chai@4.1.3":
version "4.1.3"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.3.tgz#b8a74352977a23b604c01aa784f5b793443fb7dc"
integrity sha512-f5dXGzOJycyzSMdaXVhiBhauL4dYydXwVpavfQ1mVCaGjR56a9QfklXObUxlIY9bGTmCPHEEZ04I16BZ/8w5ww==
"@types/chai@4.1.7":
version "4.1.7"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a"
integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==
"@types/cheerio@*":
version "0.22.12"
@ -1132,16 +1132,11 @@
"@types/react-native" "*"
csstype "^2.2.0"
"@types/tapable@*":
"@types/tapable@*", "@types/tapable@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370"
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@*":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082"