UNPKG

api-console-assets

Version:

This repo only exists to publish api console components to npm

581 lines (544 loc) 21 kB
<!-- @license Copyright 2016 The Advanced REST client authors <arc@mulesoft.com> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../paper-masked-input/paper-masked-input.html"> <link rel="import" href="../paper-icon-button/paper-icon-button.html"> <link rel="import" href="../paper-button/paper-button.html"> <link rel="import" href="../paper-input/paper-input.html"> <link rel="import" href="../arc-icons/arc-icons.html"> <link rel="import" href="../paper-styles/paper-styles.html"> <link rel="import" href="../iron-flex-layout/iron-flex-layout.html"> <link rel="import" href="../iron-form/iron-form.html"> <link rel="import" href="../paper-item/paper-item.html"> <link rel="import" href="../paper-toast/paper-toast.html"> <link rel="import" href="../paper-dropdown-menu/paper-dropdown-menu.html"> <link rel="import" href="../paper-listbox/paper-listbox.html"> <link rel="import" href="../paper-spinner/paper-spinner.html"> <link rel="import" href="auth-methods-behavior.html"> <!-- The `<auth-method-oauth1>` element displays a form to provide the OAuth 1a settings. ### Example ``` <auth-method-oauth1 consumer-key="xyz"></auth-method-oauth1> ``` ### Required form fields - Consumer key - Timestamp - Nonce - Signature method ## Authorizing the user This element displays form for user input only. To perform authorization and later to sign the request, add `oauth-authorization/oauth1-authorization.html` to the DOM. This element sends `oauth1-token-requested` that is handled by autorization element. Note that the OAuth1 authorization wasn't designed for browser. Most existing OAuth1 implementation disallow browsers to perform the authorization by not allowing POST requests to authorization server. Therefore receiving token may not be possible without using browser extensions to alter HTTP request to enable CORS. If the server disallow obtaining authorization token and secret from clients then the application should listen for `oauth1-token-requested` custom event and perform authorization on the server side. When application is performing authorization instead of `oauth1-authorization` element then the application should dispatch `oauth1-token-response` custom event with `oauth_token` and `oauth_token_secret` properties set on detail object. This element handles the response to reset UI state and to updates other elements status that works with authorization. ## Signing the request See description for `oauth-authorization/oauth1-authorization.html` element. ### Styling `<auth-methods>` provides the following custom properties and mixins for styling: Custom property | Description | Default ----------------|-------------|---------- `--auth-method-oauth1` | Mixin applied to the element. | `{}` `--auth-method-panel` | Mixin applied to all auth elements. | `{}` ### Theming Use this mixins as a theming option across all ARC elements. Custom property | Description | Default ----------------|-------------|---------- `--icon-button` | Mixin applied to `paper-icon-buttons`. | `{}` `--icon-button-hover` | Mixin applied to `paper-icon-buttons` when hovered. | `{}` `--input-line-color` | Mixin applied to the input underline | `{}` `--auth-button` | Mixin applied to authorization and next buttons` | `{}` `--auth-button-hover` | Mixin for :hover state for authorization and next buttons` | `{}` `--auth-button-disabled` | Mixin for disabled state for authorization and next buttons` | `{}` @group UI Elements @element auth-method-oauth1 @demo demo/oauth1.html --> <dom-module id="auth-method-oauth1"> <template> <style> :host { display: block; @apply --auth-method-panel; @apply --auth-method-oauth1; --paper-icon-button: { color: var(--hint-trigger-color, rgba(0, 0, 0, 0.54)); transition: color 0.25s linear; @apply --icon-button; } --paper-icon-button-hover: { color: var(--hint-trigger-hover-color, rgba(0, 0, 0, 0.78)); @apply --icon-button-hover; } --paper-input-container-focus-color: { @apply --input-line-color; } } .form { max-width: 700px; } .grant-dropdown { width: 320px; } .auth-button { background-color: var(--primary-color); color: rgba(255, 255, 255, 0.87); @apply --auth-button; } .auth-button:hover { @apply --auth-button-hover; } .auth-button[disabled] { background-color: rgba(0, 0, 0, 0.24); color: rgba(0, 0, 0, 0.54); @apply --auth-button-disabled; } .authorize-actions { margin-top: 12px; @apply --layout-horizontal; @apply --layout-center; } </style> <div class="form"> <form is="iron-form" id="oauth1form"> <paper-dropdown-menu label="Authorization token method" class="auth-token-method" required auto-validate> <paper-listbox class="dropdown-content" selected="{{authTokenMethod}}" attr-for-selected="data-type"> <paper-item data-type="GET">GET</paper-item> <paper-item data-type="POST">POST</paper-item> </paper-listbox> </paper-dropdown-menu> <paper-dropdown-menu label="Oauth parameters location" class="auth-params-location" required auto-validate> <paper-listbox class="dropdown-content" selected="{{authParamsLocation}}" attr-for-selected="data-type"> <paper-item data-type="querystring">Query string</paper-item> <paper-item data-type="authorization">Authorization header</paper-item> </paper-listbox> </paper-dropdown-menu> <paper-masked-input auto-validate required label="Consumer key" value="{{consumerKey}}" data-field="consumerKey" autocomplete="on"></paper-masked-input> <paper-masked-input label="Consumer secret" value="{{consumerSecret}}" data-field="consumerSecret" autocomplete="on"></paper-masked-input> <paper-masked-input auto-validate label="Token" value="{{token}}" data-field="token" autocomplete="on"></paper-masked-input> <paper-masked-input label="Token secret" value="{{tokenSecret}}" data-field="tokenSecret" autocomplete="on"></paper-masked-input> <paper-input label="Request token URL" value="{{requestTokenUrl}}"></paper-input> <paper-input label="Token Authorization URL" value="{{accessTokenUrl}}"></paper-input> <paper-input label="User authorization dialog URL" value="{{authorizationUrl}}"></paper-input> <paper-input label="Redirect URL" value="{{redirectUrl}}"></paper-input> <paper-input auto-validate required label="Timestamp" value="{{timestamp}}" type="text" data-field="timestamp" autocomplete="on"> <paper-icon-button suffix on-tap="_genTimestamp" icon="arc:cached" alt="Regenerate input icon" title="Regenerate timestamp"></paper-icon-button> <paper-icon-button suffix on-tap="_clearField" icon="arc:clear" alt="Clear input icon" title="Clear input"></paper-icon-button> </paper-input> <paper-input auto-validate required label="Nonce" value="{{nonce}}" type="text" data-field="nonce" autocomplete="on"> <paper-icon-button suffix on-tap="_genNonce" icon="arc:cached" alt="Regenerate input icon" title="Regenerate nonce"></paper-icon-button> <paper-icon-button suffix on-tap="_clearField" icon="arc:clear" alt="Clear input icon" title="Clear input"></paper-icon-button> </paper-input> <paper-input label="Realm" value="{{realm}}" type="text" data-field="realm" autocomplete="on"></paper-input> <paper-dropdown-menu label="Signature method" class="grant-dropdown" required auto-validate> <paper-listbox class="dropdown-content" selected="{{signatureMethod}}" attr-for-selected="data-type"> <template is="dom-repeat" items="[[signatureMethods]]"> <paper-item data-type$="[[item]]">[[item]]</paper-item> </template> </paper-listbox> </paper-dropdown-menu> <div class="authorize-actions"> <paper-button disabled$="[[_authorizing]]" class="auth-button" on-tap="authorize">Authorize</paper-button> <paper-spinner active="[[_authorizing]]"></paper-spinner> </div> </form> </div> <paper-toast text="" duration="5000"></paper-toast> </template> <script> Polymer({ is: 'auth-method-oauth1', behaviors: [ArcBehaviors.AuthMethodsBehavior], /** * Fired when user requested to perform an authorization. * The details object vary depends on the `grantType` property. * However this event always fire two properties set on the `detail` object: `type` and * `clientId`. * * @event oauth1-token-requested * @param {String} consumerKey The consumer key. May be undefined if not provided. * @param {String} consumerSecret May be undefined if not provided. * @param {String} token May be undefined if not provided. * @param {String} tokenSecret May be undefined if not provided. * @param {String} timestamp May be undefined if not provided. * @param {String} nonce May be undefined if not provided. * @param {String} realm May be undefined if not provided. * @param {String} signatureMethod May be undefined if not provided. * @param {String} type Always `oauth1` */ /** * Fired when the any of the auth method settings has changed. * This event will be fired quite frequently - each time anything in the text field changed. * With one exception. This event will not be fired if the validation of the form didn't passed. * * @event auth-settings-changed * @param {Object} settings Current settings. See the * `oauth1-token-requested` for detailed description. * @param {String} type The authorization type - oauth1 */ properties: { // Client ID aka consumer key consumerKey: { type: String, notify: true }, // The client secret aka consumer secret consumerSecret: { type: String, notify: true }, // Oauth 1 token (from the oauth console) token: { type: String, notify: true }, // Oauth 1 token secret (from the oauth console) tokenSecret: { type: String, notify: true }, // Timestamp timestamp: { type: Number, notify: true }, // The nonce generated for this request nonce: { type: String, notify: true }, // Optional realm realm: { type: String, notify: true }, /** * Signature method. Enum {`HMAC-SHA256`, `HMAC-SHA1`, `PLAINTEXT`} */ signatureMethod: { type: String, value: 'HMAC-SHA1', notify: true }, // True when currently authorizing the user. _authorizing: Boolean, /** * Authorization callback URL */ redirectUrl: String, /** * OAuth1 endpoint to obtain request token to request user authorization. */ requestTokenUrl: String, /** * Endpoint to authorize the token. */ accessTokenUrl: String, /** * HTTP method to obtain authorization header. * Spec recommends POST */ authTokenMethod: { type: String, value: 'POST' }, /** * A location of the OAuth 1 authorization parameters. * It can be either in the URL as a query string (`querystring` value) * or in the authorization header (`authorization`) value. */ authParamsLocation: { type: String, value: 'authorization' }, /** * An URL to authentication endpoint where the user should be redirected * to auththorize the app. */ authorizationUrl: String, /** * RAML `securedBy` obejct definition. * If set, it will prefill the settings in the auth panel. */ ramlSettings: { type: Object, observer: '_ramlSettingsChanged' }, /** * List of currently support signature methods. * This can be updated when `ramlSettings` property is set. */ signatureMethods: Array }, /** * Returns default list of signature methods for OAuth1 */ get defaultSignatureMethods() { return ['HMAC-SHA1', 'RSA-SHA1', 'PLAINTEXT']; }, observers: [ //jscs:disable '_settingsChanged(consumerKey, consumerSecret, token, tokenSecret, timestamp, nonce, realm, signatureMethod, redirectUrl, requestTokenUrl, accessTokenUrl, authTokenMethod, authParamsLocation, authorizationUrl)' //jscs:enable ], _attachListeners: function(node) { this.listen(node, 'oauth1-error', '_oauth1ErrorHandler'); this.listen(node, 'oauth1-token-response', '_tokenResponseHandler'); }, _detachListeners: function(node) { this.unlisten(node, 'oauth1-error', '_oauth1ErrorHandler'); this.unlisten(node, 'oauth1-token-response', '_tokenResponseHandler'); }, ready: function() { this._genTimestamp(); this._genNonce(); if (!this.signatureMethods) { this.signatureMethods = this.defaultSignatureMethods; } }, /** * Gets a map of current settings (user provided data). */ get settings() { return this._getSettings(); }, /** * Validate the form. * * @param {Boolean} passive If true then it will check validity of the form but will not * highlight the fields when invalid. Default to false. * @return {Boolean} `true` if valid, `false` otherwise. */ validate: function(passive) { return passive ? this.$.oauth1form.checkValidity() : this.$.oauth1form.validate(); }, /** * Called each time when any of the settings change. It informs application * that the user updated the form. * It fires `auth-settings-changed` custom event even if the form is invalid * (missing some info). * * The `valid` property is always if `settings.token` amd * `settings.tokenSecret` is not set. */ _settingsChanged: function() { var validationResult = this.$.oauth1form.validate(); var settings = this._getSettings(); if (validationResult) { if (!settings || !settings.token || !settings.tokenSecret) { validationResult = false; } } var detail = { settings: settings, type: 'oauth1', valid: validationResult }; this.fire('auth-settings-changed', detail); }, _getSettings: function() { return { consumerKey: this.consumerKey, consumerSecret: this.consumerSecret, token: this.token, tokenSecret: this.tokenSecret, timestamp: this.timestamp, nonce: this.nonce, realm: this.realm, signatureMethod: this.signatureMethod, requestTokenUrl: this.requestTokenUrl, accessTokenUrl: this.accessTokenUrl, redirectUrl: this.redirectUrl, authTokenMethod: this.authTokenMethod, authParamsLocation: this.authParamsLocation, authorizationUrl: this.authorizationUrl, type: 'oauth1' }; }, /** * Restores settings from stored value. * * @param {Object} settings Object returned by `_getSettings()` */ restore: function(settings) { this.consumerKey = settings.consumerKey; this.consumerSecret = settings.consumerSecret; this.token = settings.token; this.tokenSecret = settings.tokenSecret; this.timestamp = settings.timestamp; this.nonce = settings.nonce; this.realm = settings.realm; this.signatureMethod = settings.signatureMethod; this.requestTokenUrl = settings.requestTokenUrl; this.accessTokenUrl = settings.accessTokenUrl; this.redirectUrl = settings.redirectUrl; this.authTokenMethod = settings.authTokenMethod; this.authParamsLocation = settings.authParamsLocation; this.authorizationUrl = settings.authorizationUrl; }, // Removes a value from the (paper-)input going up through path of the event. _clearField: function(e) { e = Polymer.dom(e); var path = e.path; var inputTarget; while ((inputTarget = path.shift())) { if (inputTarget.nodeName === 'INPUT' || inputTarget.nodeName === 'PAPER-INPUT') { break; } } if (!inputTarget) { return; } inputTarget.value = ''; }, // Handler for "authorize" button click. Sends the `oauth2-token-requested` event. authorize: function() { this._authorizing = true; var detail = {}; if (this.consumerKey) { detail.consumerKey = this.consumerKey; } if (this.consumerSecret) { detail.consumerSecret = this.consumerSecret; } if (this.token) { detail.token = this.token; } if (this.tokenSecret) { detail.tokenSecret = this.tokenSecret; } if (this.timestamp) { detail.timestamp = this.timestamp; } if (this.nonce) { detail.nonce = this.nonce; } if (this.realm) { detail.realm = this.realm; } if (this.signatureMethod) { detail.signatureMethod = this.signatureMethod; } if (this.requestTokenUrl) { detail.requestTokenUrl = this.requestTokenUrl; } if (this.accessTokenUrl) { detail.accessTokenUrl = this.accessTokenUrl; } if (this.redirectUrl) { detail.redirectUrl = this.redirectUrl; } if (this.authParamsLocation) { detail.authParamsLocation = this.authParamsLocation; } if (this.authTokenMethod) { detail.authTokenMethod = this.authTokenMethod; } if (this.authorizationUrl) { detail.authorizationUrl = this.authorizationUrl; } detail.type = 'oauth1'; this.fire('oauth1-token-requested', detail); }, /** * Handles OAuth1 authorization errors. */ _oauth1ErrorHandler: function(e, detail) { this._authorizing = false; var toast = this.$$('paper-toast'); toast.text = detail.message; toast.opened = true; }, // jscs:disable requireCamelCaseOrUpperCaseIdentifiers // jscs:disable requireDotNotation /** * Handler for the `oauth1-token-response` custom event. * Sets `token` and `tokenSecret` properties from the event. */ _tokenResponseHandler: function(e) { this._authorizing = false; this.token = e.detail.oauth_token; this.tokenSecret = e.detail.oauth_token_secret; }, // Returns current timestamp in seconds _genTimestamp: function() { var t = Math.floor(Date.now() / 1000); this.timestamp = t; }, /** * Returns autogenerated nocne * @param {?Number} length Optional, size of generated string. Default to 32. * @return {String} Generated nonce string. */ _genNonce: function(length) { var result = []; var chrs = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; var chrsLength = chrs.length; length = Number(length || 32); if (length !== length) { length = 32; } for (var i = 0; i < length; i++) { result[result.length] = (chrs[Math.floor(Math.random() * chrsLength)]); } this.nonce = result.join(''); }, /** * Called when the RAML object change */ _ramlSettingsChanged: function(type) { if (!type) { type = {}; } if (!type.settings) { type.settings = {}; } this.preFill(type.settings); }, /** * Pre fills the form with RAML defined settings. * * @param {Object} settings The settings object of the OAuth1 security schema. */ preFill: function(settings) { if (settings.requestTokenUri) { this.requestTokenUrl = settings.requestTokenUri; } if (settings.authorizationUri) { this.authorizationUrl = settings.authorizationUri; } if (settings.tokenCredentialsUri) { this.accessTokenUrl = settings.tokenCredentialsUri; } if (settings.signatures) { this.signatureMethods = settings.signatures; } else { this.signatureMethods = this.defaultSignatureMethods; } } }); </script> </dom-module>