Skip to content

Commit

Permalink
Merge pull request #3780 from swagger-api/ft/oas3-authorization
Browse files Browse the repository at this point in the history
OAS 3.0 Authorization
  • Loading branch information
shockey authored Oct 21, 2017
2 parents 353fd00 + 7c91732 commit c46e18d
Show file tree
Hide file tree
Showing 16 changed files with 435 additions and 56 deletions.
5 changes: 4 additions & 1 deletion dev-helpers/oauth2-redirect.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@

isValid = qp.state === sentState

if (oauth2.auth.schema.get("flow") === "accessCode" && !oauth2.auth.code) {
if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
Expand Down
5 changes: 4 additions & 1 deletion dist/oauth2-redirect.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@

isValid = qp.state === sentState

if (oauth2.auth.schema.get("flow") === "accessCode" && !oauth2.auth.code) {
if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
Expand Down
9 changes: 5 additions & 4 deletions src/core/components/auth/api-key-auth.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,15 @@ export default class ApiKeyAuth extends React.Component {

return (
<div>
<h4>Api key authorization<JumpToPath path={[ "securityDefinitions", name ]} /></h4>
<h4>
<code>{ name || schema.get("name") }</code>&nbsp;
(apiKey)
<JumpToPath path={[ "securityDefinitions", name ]} />
</h4>
{ value && <h6>Authorized</h6>}
<Row>
<Markdown source={ schema.get("description") } />
</Row>
<Row>
<p>Name: <code>{ schema.get("name") }</code></p>
</Row>
<Row>
<p>In: <code>{ schema.get("in") }</code></p>
</Row>
Expand Down
62 changes: 62 additions & 0 deletions src/core/components/auth/auth-item.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"

export default class Auths extends React.Component {
static propTypes = {
schema: ImPropTypes.orderedMap.isRequired,
name: PropTypes.string.isRequired,
onAuthChange: PropTypes.func.isRequired,
authorized: ImPropTypes.orderedMap.isRequired
}

render() {
let {
schema,
name,
getComponent,
onAuthChange,
authorized,
errSelectors
} = this.props
const ApiKeyAuth = getComponent("apiKeyAuth")
const BasicAuth = getComponent("basicAuth")

let authEl

const type = schema.get("type")

switch(type) {
case "apiKey": authEl = <ApiKeyAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ onAuthChange } />
break
case "basic": authEl = <BasicAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ onAuthChange } />
break
default: authEl = <div key={ name }>Unknown security definition type { type }</div>
}

return (<div key={`${name}-jump`}>
{ authEl }
</div>)
}

static propTypes = {
errSelectors: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
authSelectors: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
definitions: ImPropTypes.iterable.isRequired
}
}
40 changes: 10 additions & 30 deletions src/core/components/auth/auths.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export default class Auths extends React.Component {
e.preventDefault()

let { authActions } = this.props

authActions.authorize(this.state)
}

Expand All @@ -44,8 +43,7 @@ export default class Auths extends React.Component {

render() {
let { definitions, getComponent, authSelectors, errSelectors } = this.props
const ApiKeyAuth = getComponent("apiKeyAuth")
const BasicAuth = getComponent("basicAuth")
const AuthItem = getComponent("AuthItem")
const Oauth2 = getComponent("oauth2", true)
const Button = getComponent("Button")

Expand All @@ -64,33 +62,15 @@ export default class Auths extends React.Component {
!!nonOauthDefinitions.size && <form onSubmit={ this.submitAuth }>
{
nonOauthDefinitions.map( (schema, name) => {
let type = schema.get("type")
let authEl

switch(type) {
case "apiKey": authEl = <ApiKeyAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ this.onAuthChange } />
break
case "basic": authEl = <BasicAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ this.onAuthChange } />
break
default: authEl = <div key={ name }>Unknown security definition type { type }</div>
}

return (<div key={`${name}-jump`}>
{ authEl }
</div>)

return <AuthItem
key={name}
schema={schema}
name={name}
getComponent={getComponent}
onAuthChange={this.onAuthChange}
authorized={authorized}
errSelectors={errSelectors}
/>
}).toArray()
}
<div className="auth-btn-wrapper">
Expand Down
20 changes: 13 additions & 7 deletions src/core/components/auth/oauth2.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ import React from "react"
import PropTypes from "prop-types"
import oauth2Authorize from "core/oauth2-authorize"

const IMPLICIT = "implicit"
const ACCESS_CODE = "accessCode"
const PASSWORD = "password"
const APPLICATION = "application"

export default class Oauth2 extends React.Component {
static propTypes = {
name: PropTypes.string,
Expand All @@ -16,6 +11,7 @@ export default class Oauth2 extends React.Component {
authSelectors: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
errSelectors: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
errActions: PropTypes.object.isRequired,
getConfigs: PropTypes.any
}
Expand Down Expand Up @@ -83,7 +79,9 @@ export default class Oauth2 extends React.Component {
}

render() {
let { schema, getComponent, authSelectors, errSelectors, name } = this.props
let {
schema, getComponent, authSelectors, errSelectors, name, specSelectors
} = this.props
const Input = getComponent("Input")
const Row = getComponent("Row")
const Col = getComponent("Col")
Expand All @@ -92,6 +90,14 @@ export default class Oauth2 extends React.Component {
const JumpToPath = getComponent("JumpToPath", true)
const Markdown = getComponent( "Markdown" )

const { isOAS3 } = specSelectors

// Auth type consts
const IMPLICIT = "implicit"
const PASSWORD = "password"
const ACCESS_CODE = isOAS3() ? "authorizationCode" : "accessCode"
const APPLICATION = isOAS3() ? "clientCredentials" : "application"

let flow = schema.get("flow")
let scopes = schema.get("allowedScopes") || schema.get("scopes")
let authorizedAuth = authSelectors.authorized().get(name)
Expand All @@ -102,7 +108,7 @@ export default class Oauth2 extends React.Component {

return (
<div>
<h4>OAuth2.0 <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
<h4>{name} (OAuth2, { schema.get("flow") }) <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
{ !this.state.appName ? null : <h5>Application: { this.state.appName } </h5> }
{ description && <Markdown source={ schema.get("description") } /> }

Expand Down
10 changes: 10 additions & 0 deletions src/core/oauth2-authorize.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ export default function authorize ( { auth, authActions, errActions, configs, au
case "implicit":
query.push("response_type=token")
break

case "clientCredentials":
// OAS3
authActions.authorizeApplication(auth)
return

case "authorizationCode":
// OAS3
query.push("response_type=code")
break
}

if (typeof clientId === "string") {
Expand Down
2 changes: 1 addition & 1 deletion src/core/plugins/auth/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default {
securities.entrySeq().forEach( ([ key, security ]) => {
let type = security.getIn(["schema", "type"])

if ( type === "apiKey" ) {
if ( type === "apiKey" || type === "http" ) {
map = map.set(key, security)
} else if ( type === "basic" ) {
let username = security.getIn(["value", "username"])
Expand Down
53 changes: 42 additions & 11 deletions src/core/plugins/oas3/auth-extensions/wrap-selectors.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,60 @@
import { createSelector } from "reselect"
import { List } from "immutable"
import { List, Map, fromJS } from "immutable"
import { isOAS3 as isOAS3Helper } from "../helpers"


// Helpers

const state = state => state

function onlyOAS3(selector) {
return (ori, system) => (state, ...args) => {
const spec = system.getSystem().specSelectors.specJson()
if(isOAS3Helper(spec)) {
return selector(...args)
return selector(system, ...args)
} else {
return ori(...args)
}
}
}

const nullSelector = createSelector(() => null)
export const definitionsToAuthorize = onlyOAS3(createSelector(
state,
({ specSelectors }) => {
// Coerce our OpenAPI 3.0 definitions into monoflow definitions
// that look like Swagger2 definitions.
let definitions = specSelectors.securityDefinitions()
let list = List()

definitions.entrySeq().forEach( ([ defName, definition ]) => {
const type = definition.get("type")

const OAS3NullSelector = onlyOAS3(nullSelector)
if(type === "oauth2") {
definition.get("flows").entrySeq().forEach(([flowKey, flowVal]) => {
let translatedDef = fromJS({
flow: flowKey,
authorizationUrl: flowVal.get("authorizationUrl"),
tokenUrl: flowVal.get("tokenUrl"),
scopes: flowVal.get("scopes"),
type: definition.get("type")
})

// Hasta la vista, authorization!
export const shownDefinitions = OAS3NullSelector
export const definitionsToAuthorize = OAS3NullSelector
export const getDefinitionsByNames = OAS3NullSelector
export const authorized = onlyOAS3(() => List())
export const isAuthorized = OAS3NullSelector
export const getConfigs = OAS3NullSelector
list = list.push(new Map({
[defName]: translatedDef.filter((v) => {
// filter out unset values, sometimes `authorizationUrl`
// and `tokenUrl` come out as `undefined` in the data
return v !== undefined
})
}))
})
}
if(type === "http" || type === "apiKey") {
list = list.push(new Map({
[defName]: definition
}))
}
})

return list
}
))
Loading

0 comments on commit c46e18d

Please sign in to comment.