oauth2.jsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. import React from "react"
  2. import PropTypes from "prop-types"
  3. import oauth2Authorize from "core/oauth2-authorize"
  4. export default class Oauth2 extends React.Component {
  5. static propTypes = {
  6. name: PropTypes.string,
  7. authorized: PropTypes.object,
  8. getComponent: PropTypes.func.isRequired,
  9. schema: PropTypes.object.isRequired,
  10. authSelectors: PropTypes.object.isRequired,
  11. authActions: PropTypes.object.isRequired,
  12. errSelectors: PropTypes.object.isRequired,
  13. oas3Selectors: PropTypes.object.isRequired,
  14. specSelectors: PropTypes.object.isRequired,
  15. errActions: PropTypes.object.isRequired,
  16. getConfigs: PropTypes.any
  17. }
  18. constructor(props, context) {
  19. super(props, context)
  20. let { name, schema, authorized, authSelectors } = this.props
  21. let auth = authorized && authorized.get(name)
  22. let authConfigs = authSelectors.getConfigs() || {}
  23. let username = auth && auth.get("username") || ""
  24. let clientId = auth && auth.get("clientId") || authConfigs.clientId || ""
  25. let clientSecret = auth && auth.get("clientSecret") || authConfigs.clientSecret || ""
  26. let passwordType = auth && auth.get("passwordType") || "basic"
  27. let scopes = auth && auth.get("scopes") || authConfigs.scopes || []
  28. if (typeof scopes === "string") {
  29. scopes = scopes.split(authConfigs.scopeSeparator || " ")
  30. }
  31. this.state = {
  32. appName: authConfigs.appName,
  33. name: name,
  34. schema: schema,
  35. scopes: scopes,
  36. clientId: clientId,
  37. clientSecret: clientSecret,
  38. username: username,
  39. password: "",
  40. passwordType: passwordType
  41. }
  42. }
  43. close = (e) => {
  44. e.preventDefault()
  45. let { authActions } = this.props
  46. authActions.showDefinitions(false)
  47. }
  48. authorize =() => {
  49. let { authActions, errActions, getConfigs, authSelectors, oas3Selectors } = this.props
  50. let configs = getConfigs()
  51. let authConfigs = authSelectors.getConfigs()
  52. errActions.clear({authId: name,type: "auth", source: "auth"})
  53. oauth2Authorize({
  54. auth: this.state,
  55. currentServer: oas3Selectors.serverEffectiveValue(oas3Selectors.selectedServer()),
  56. authActions,
  57. errActions,
  58. configs,
  59. authConfigs
  60. })
  61. }
  62. onScopeChange =(e) => {
  63. let { target } = e
  64. let { checked } = target
  65. let scope = target.dataset.value
  66. if ( checked && this.state.scopes.indexOf(scope) === -1 ) {
  67. let newScopes = this.state.scopes.concat([scope])
  68. this.setState({ scopes: newScopes })
  69. } else if ( !checked && this.state.scopes.indexOf(scope) > -1) {
  70. this.setState({ scopes: this.state.scopes.filter((val) => val !== scope) })
  71. }
  72. }
  73. onInputChange =(e) => {
  74. let { target : { dataset : { name }, value } } = e
  75. let state = {
  76. [name]: value
  77. }
  78. this.setState(state)
  79. }
  80. selectScopes =(e) => {
  81. if (e.target.dataset.all) {
  82. this.setState({
  83. scopes: Array.from((this.props.schema.get("allowedScopes") || this.props.schema.get("scopes")).keys())
  84. })
  85. } else {
  86. this.setState({ scopes: [] })
  87. }
  88. }
  89. logout =(e) => {
  90. e.preventDefault()
  91. let { authActions, errActions, name } = this.props
  92. errActions.clear({authId: name, type: "auth", source: "auth"})
  93. authActions.logoutWithPersistOption([ name ])
  94. }
  95. render() {
  96. let {
  97. schema, getComponent, authSelectors, errSelectors, name, specSelectors
  98. } = this.props
  99. const Input = getComponent("Input")
  100. const Row = getComponent("Row")
  101. const Col = getComponent("Col")
  102. const Button = getComponent("Button")
  103. const AuthError = getComponent("authError")
  104. const JumpToPath = getComponent("JumpToPath", true)
  105. const Markdown = getComponent("Markdown", true)
  106. const InitializedInput = getComponent("InitializedInput")
  107. const { isOAS3 } = specSelectors
  108. let oidcUrl = isOAS3() ? schema.get("openIdConnectUrl") : null
  109. // Auth type consts
  110. const IMPLICIT = "implicit"
  111. const PASSWORD = "password"
  112. const ACCESS_CODE = isOAS3() ? (oidcUrl ? "authorization_code" : "authorizationCode") : "accessCode"
  113. const APPLICATION = isOAS3() ? (oidcUrl ? "client_credentials" : "clientCredentials") : "application"
  114. let flow = schema.get("flow")
  115. let scopes = schema.get("allowedScopes") || schema.get("scopes")
  116. let authorizedAuth = authSelectors.authorized().get(name)
  117. let isAuthorized = !!authorizedAuth
  118. let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
  119. let isValid = !errors.filter( err => err.get("source") === "validation").size
  120. let description = schema.get("description")
  121. return (
  122. <div>
  123. <h4>{name} (OAuth2, { schema.get("flow") }) <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
  124. { !this.state.appName ? null : <h5>Application: { this.state.appName } </h5> }
  125. { description && <Markdown source={ schema.get("description") } /> }
  126. { isAuthorized && <h6>Authorized</h6> }
  127. { oidcUrl && <p>OpenID Connect URL: <code>{ oidcUrl }</code></p> }
  128. { ( flow === IMPLICIT || flow === ACCESS_CODE ) && <p>Authorization URL: <code>{ schema.get("authorizationUrl") }</code></p> }
  129. { ( flow === PASSWORD || flow === ACCESS_CODE || flow === APPLICATION ) && <p>Token URL:<code> { schema.get("tokenUrl") }</code></p> }
  130. <p className="flow">Flow: <code>{ schema.get("flow") }</code></p>
  131. {
  132. flow !== PASSWORD ? null
  133. : <Row>
  134. <Row>
  135. <label htmlFor="oauth_username">username:</label>
  136. {
  137. isAuthorized ? <code> { this.state.username } </code>
  138. : <Col tablet={10} desktop={10}>
  139. <input id="oauth_username" type="text" data-name="username" onChange={ this.onInputChange } autoFocus/>
  140. </Col>
  141. }
  142. </Row>
  143. {
  144. }
  145. <Row>
  146. <label htmlFor="oauth_password">password:</label>
  147. {
  148. isAuthorized ? <code> ****** </code>
  149. : <Col tablet={10} desktop={10}>
  150. <input id="oauth_password" type="password" data-name="password" onChange={ this.onInputChange }/>
  151. </Col>
  152. }
  153. </Row>
  154. <Row>
  155. <label htmlFor="password_type">Client credentials location:</label>
  156. {
  157. isAuthorized ? <code> { this.state.passwordType } </code>
  158. : <Col tablet={10} desktop={10}>
  159. <select id="password_type" data-name="passwordType" onChange={ this.onInputChange }>
  160. <option value="basic">Authorization header</option>
  161. <option value="request-body">Request body</option>
  162. </select>
  163. </Col>
  164. }
  165. </Row>
  166. </Row>
  167. }
  168. {
  169. ( flow === APPLICATION || flow === IMPLICIT || flow === ACCESS_CODE || flow === PASSWORD ) &&
  170. ( !isAuthorized || isAuthorized && this.state.clientId) && <Row>
  171. <label htmlFor="client_id">client_id:</label>
  172. {
  173. isAuthorized ? <code> ****** </code>
  174. : <Col tablet={10} desktop={10}>
  175. <InitializedInput id="client_id"
  176. type="text"
  177. required={ flow === PASSWORD }
  178. initialValue={ this.state.clientId }
  179. data-name="clientId"
  180. onChange={ this.onInputChange }/>
  181. </Col>
  182. }
  183. </Row>
  184. }
  185. {
  186. ( (flow === APPLICATION || flow === ACCESS_CODE || flow === PASSWORD) && <Row>
  187. <label htmlFor="client_secret">client_secret:</label>
  188. {
  189. isAuthorized ? <code> ****** </code>
  190. : <Col tablet={10} desktop={10}>
  191. <InitializedInput id="client_secret"
  192. initialValue={ this.state.clientSecret }
  193. type="password"
  194. data-name="clientSecret"
  195. onChange={ this.onInputChange }/>
  196. </Col>
  197. }
  198. </Row>
  199. )}
  200. {
  201. !isAuthorized && scopes && scopes.size ? <div className="scopes">
  202. <h2>
  203. Scopes:
  204. <a onClick={this.selectScopes} data-all={true}>select all</a>
  205. <a onClick={this.selectScopes}>select none</a>
  206. </h2>
  207. { scopes.map((description, name) => {
  208. return (
  209. <Row key={ name }>
  210. <div className="checkbox">
  211. <Input data-value={ name }
  212. id={`${name}-${flow}-checkbox-${this.state.name}`}
  213. disabled={ isAuthorized }
  214. checked={ this.state.scopes.includes(name) }
  215. type="checkbox"
  216. onChange={ this.onScopeChange }/>
  217. <label htmlFor={`${name}-${flow}-checkbox-${this.state.name}`}>
  218. <span className="item"></span>
  219. <div className="text">
  220. <p className="name">{name}</p>
  221. <p className="description">{description}</p>
  222. </div>
  223. </label>
  224. </div>
  225. </Row>
  226. )
  227. }).toArray()
  228. }
  229. </div> : null
  230. }
  231. {
  232. errors.valueSeq().map( (error, key) => {
  233. return <AuthError error={ error }
  234. key={ key }/>
  235. } )
  236. }
  237. <div className="auth-btn-wrapper">
  238. { isValid &&
  239. ( isAuthorized ? <Button className="btn modal-btn auth authorize" onClick={ this.logout }>Logout</Button>
  240. : <Button className="btn modal-btn auth authorize" onClick={ this.authorize }>Authorize</Button>
  241. )
  242. }
  243. <Button className="btn modal-btn auth btn-done" onClick={ this.close }>Close</Button>
  244. </div>
  245. </div>
  246. )
  247. }
  248. }