1
0
Fork 0

distinguish between 2 legged and 3 legged oauth && other authorization improvements

Dieser Commit ist enthalten in:
Angus McLeod 2019-06-07 13:09:31 +10:00
Ursprung 4f195c704a
Commit b9f8cc61b2
6 geänderte Dateien mit 147 neuen und 93 gelöschten Zeilen

Datei anzeigen

@ -7,16 +7,16 @@ export default Ember.Controller.extend({
queryParams: ['refresh_list'], queryParams: ['refresh_list'],
loadingSubscriptions: false, loadingSubscriptions: false,
notAuthorized: Ember.computed.not('api.authorized'), notAuthorized: Ember.computed.not('api.authorized'),
authorizationTypes: ['oauth', 'basic'],
isOauth: Ember.computed.equal('api.authType', 'oauth'),
isBasicAuth: Ember.computed.equal('api.authType', 'basic'),
endpointMethods: ['GET', 'PUT', 'POST', 'PATCH', 'DELETE'], endpointMethods: ['GET', 'PUT', 'POST', 'PATCH', 'DELETE'],
showRemove: Ember.computed.not('isNew'), showRemove: Ember.computed.not('isNew'),
showRedirectUri: Ember.computed.and('threeLeggedOauth', 'api.name'),
responseIcon: null, responseIcon: null,
@computed('saveDisabled', 'api.authType', 'api.authUrl', 'api.clientId', 'api.clientSecret') @computed('saveDisabled', 'api.authType', 'api.authUrl', 'api.tokenUrl', 'api.clientId', 'api.clientSecret', 'threeLeggedOauth')
authDisabled(saveDisabled, authType, authUrl, clientId, clientSecret) { authDisabled(saveDisabled, authType, authUrl, tokenUrl, clientId, clientSecret, threeLeggedOauth) {
return saveDisabled || !authType || !authUrl || !clientId || !clientSecret; if (saveDisabled || !authType || !tokenUrl || !clientId || !clientSecret) return true;
if (threeLeggedOauth) return !authUrl;
return false;
}, },
@computed('api.name', 'api.authType') @computed('api.name', 'api.authType')
@ -24,6 +24,17 @@ export default Ember.Controller.extend({
return !name || !authType; return !name || !authType;
}, },
authorizationTypes: ['basic', 'oauth_2', 'oauth_3'],
isBasicAuth: Ember.computed.equal('api.authType', 'basic'),
@computed('api.authType')
isOauth(authType) {
return authType && authType.indexOf('oauth') > -1;
},
twoLeggedOauth: Ember.computed.equal('api.authType', 'oauth_2'),
threeLeggedOauth: Ember.computed.equal('api.authType', 'oauth_3'),
actions: { actions: {
addParam() { addParam() {
this.get('api.authParams').pushObject({}); this.get('api.authParams').pushObject({});
@ -43,23 +54,40 @@ export default Ember.Controller.extend({
authorize() { authorize() {
const api = this.get('api'); const api = this.get('api');
const { authType, authUrl, authParams } = api; const { name, authType, authUrl, authParams } = api;
if (authType !== 'oauth') return; this.set('authErrorMessage', '');
let query = '?'; if (authType === 'oauth_2') {
this.set('authorizing', true);
ajax(`/admin/wizards/apis/${name.underscore()}/authorize`).catch(popupAjaxError)
.then(result => {
if (result.success) {
this.set('api', CustomWizardApi.create(result.api));
} else if (result.failed && result.message) {
this.set('authErrorMessage', result.message);
} else {
this.set('authErrorMessage', 'Authorization Failed');
}
setTimeout(() => {
this.set('authErrorMessage', '');
}, 6000);
}).finally(() => this.set('authorizing', false));
} else if (authType === 'oauth_3') {
let query = '?';
query += `client_id=${api.clientId}`; query += `client_id=${api.clientId}`;
query += `&redirect_uri=${encodeURIComponent(api.redirectUri)}`; query += `&redirect_uri=${encodeURIComponent(api.redirectUri)}`;
query += `&response_type=code`; query += `&response_type=code`;
if (authParams) { if (authParams) {
authParams.forEach(p => { authParams.forEach(p => {
query += `&${p.key}=${encodeURIComponent(p.value)}`; query += `&${p.key}=${encodeURIComponent(p.value)}`;
}); });
}
window.location.href = authUrl + query;
} }
window.location.href = authUrl + query;
}, },
save() { save() {
@ -88,10 +116,12 @@ export default Ember.Controller.extend({
let requiredParams; let requiredParams;
if (authType === 'oauth') { if (authType === 'basic') {
requiredParams = ['authUrl', 'tokenUrl', 'clientId', 'clientSecret'];
} else if (authType === 'basic') {
requiredParams = ['username', 'password']; requiredParams = ['username', 'password'];
} else if (authType === 'oauth_2') {
requiredParams = ['tokenUrl', 'clientId', 'clientSecret'];
} else if (authType === 'oauth_3') {
requiredParams = ['authUrl', 'tokenUrl', 'clientId', 'clientSecret'];
} }
for (let rp of requiredParams) { for (let rp of requiredParams) {

Datei anzeigen

@ -49,6 +49,13 @@
<div class="wizard-api-header"> <div class="wizard-api-header">
<div class="buttons"> <div class="buttons">
{{#if isOauth}} {{#if isOauth}}
{{#if authorizing}}
{{loading-spinner size="small"}}
{{else}}
{{#if authErrorMessage}}
<span>{{authErrorMessage}}</span>
{{/if}}
{{/if}}
{{d-button label="admin.wizard.api.auth.btn" {{d-button label="admin.wizard.api.auth.btn"
action="authorize" action="authorize"
disabled=authDisabled disabled=authDisabled
@ -68,17 +75,15 @@
{{i18n 'admin.wizard.api.auth.settings'}} {{i18n 'admin.wizard.api.auth.settings'}}
</div> </div>
{{#if isOauth}} {{#if showRedirectUri}}
{{#if api.name}} <div class="control-group redirect-uri">
<div class="control-group redirect-uri"> <div class="control-label">
<div class="control-label"> <label>{{i18n 'admin.wizard.api.auth.redirect_uri'}}</label>
<label>{{i18n 'admin.wizard.api.auth.redirect_uri'}}</label> <div class="controls">
<div class="controls"> {{api.redirectUri}}
{{api.redirectUri}}
</div>
</div> </div>
</div> </div>
{{/if}} </div>
{{/if}} {{/if}}
<div class="control-group auth-type"> <div class="control-group auth-type">
@ -89,12 +94,14 @@
</div> </div>
{{#if isOauth}} {{#if isOauth}}
<div class="control-group"> {{#if threeLeggedOauth}}
<label>{{i18n 'admin.wizard.api.auth.url'}}</label> <div class="control-group">
<div class="controls"> <label>{{i18n 'admin.wizard.api.auth.url'}}</label>
{{input value=api.authUrl}} <div class="controls">
{{input value=api.authUrl}}
</div>
</div> </div>
</div> {{/if}}
<div class="control-group"> <div class="control-group">
<label>{{i18n 'admin.wizard.api.auth.token_url'}}</label> <label>{{i18n 'admin.wizard.api.auth.token_url'}}</label>
@ -165,12 +172,14 @@
{{i18n 'admin.wizard.api.status.label'}} {{i18n 'admin.wizard.api.status.label'}}
</div> </div>
<div class="control-group"> {{#if threeLeggedOauth}}
<label>{{i18n 'admin.wizard.api.status.code'}}</label> <div class="control-group">
<div class="controls"> <label>{{i18n 'admin.wizard.api.status.code'}}</label>
{{api.code}} <div class="controls">
{{api.code}}
</div>
</div> </div>
</div> {{/if}}
<div class="control-group"> <div class="control-group">
<label>{{i18n 'admin.wizard.api.status.access_token'}}</label> <label>{{i18n 'admin.wizard.api.status.access_token'}}</label>
@ -179,12 +188,14 @@
</div> </div>
</div> </div>
<div class="control-group"> {{#if threeLeggedOauth}}
<label>{{i18n 'admin.wizard.api.status.refresh_token'}}</label> <div class="control-group">
<div class="controls"> <label>{{i18n 'admin.wizard.api.status.refresh_token'}}</label>
{{api.refreshToken}} <div class="controls">
{{api.refreshToken}}
</div>
</div> </div>
</div> {{/if}}
<div class="control-group"> <div class="control-group">
<label>{{i18n 'admin.wizard.api.status.expires_at'}}</label> <label>{{i18n 'admin.wizard.api.status.expires_at'}}</label>

Datei anzeigen

@ -63,6 +63,21 @@ class CustomWizard::ApiController < ::ApplicationController
render json: success_json render json: success_json
end end
def authorize
result = CustomWizard::Api::Authorization.get_token(api_params[:name])
if result['error']
render json: failed_json.merge(message: result['error_description'] || result['error'])
else
render json: success_json.merge(
api: CustomWizard::ApiSerializer.new(
CustomWizard::Api.get(api_params[:name]),
root: false
)
)
end
end
def clearlogs def clearlogs
CustomWizard::Api::LogEntry.clear(api_params[:name]) CustomWizard::Api::LogEntry.clear(api_params[:name])
render json: success_json render json: success_json

Datei anzeigen

@ -1,7 +1,7 @@
module Jobs module Jobs
class RefreshApiAccessToken < Jobs::Base class RefreshApiAccessToken < Jobs::Base
def execute(args) def execute(args)
CustomWizard::Api::Authorization.refresh_token(args[:name]) CustomWizard::Api::Authorization.get_token(args[:name], refresh: true)
end end
end end
end end

Datei anzeigen

@ -77,71 +77,68 @@ class CustomWizard::Api::Authorization
end end
end end
def self.get_token(name) def self.get_token(name, opts = {})
authorization = CustomWizard::Api::Authorization.get(name) authorization = CustomWizard::Api::Authorization.get(name)
type = authorization.auth_type
body = { body = {}
client_id: authorization.client_id,
client_secret: authorization.client_secret,
code: authorization.code,
grant_type: 'authorization_code',
redirect_uri: Discourse.base_url + "/admin/wizards/apis/#{name}/redirect"
}
result = Excon.post( if opts[:refresh] && type === 'oauth_3'
body['grant_type'] = 'refresh_token'
elsif type === 'oauth_2'
body['grant_type'] = 'client_credentials'
elsif type === 'oauth_3'
body['grant_type'] = 'authorization_code'
end
unless opts[:refresh]
body['client_id'] = authorization.client_id
body['client_secret'] = authorization.client_secret
end
if type === 'oauth_3'
body['code'] = authorization.code
body['redirect_uri'] = Discourse.base_url + "/admin/wizards/apis/#{name}/redirect"
end
connection = Excon.new(
authorization.token_url, authorization.token_url,
:headers => { :headers => {
"Content-Type" => "application/x-www-form-urlencoded" "Content-Type" => "application/x-www-form-urlencoded"
}, },
:body => URI.encode_www_form(body) :method => 'GET',
:query => URI.encode_www_form(body)
) )
self.handle_token_result(name, result) result = connection.request()
end
def self.refresh_token(name)
authorization = CustomWizard::Api::Authorization.get(name)
body = {
grant_type: 'refresh_token',
refresh_token: authorization.refresh_token
}
authorization_string = authorization.client_id + ':' + authorization.client_secret
result = Excon.post(
authorization.token_url,
:headers => {
"Content-Type" => "application/x-www-form-urlencoded",
"Authorization" => "Basic #{Base64.strict_encode64(authorization_string)}"
},
:body => URI.encode_www_form(body)
)
self.handle_token_result(name, result) self.handle_token_result(name, result)
end end
def self.handle_token_result(name, result) def self.handle_token_result(name, result)
data = JSON.parse(result.body) result_data = JSON.parse(result.body)
return false if (data['error']) if result_data['error']
return result_data
end
access_token = data['access_token'] data = {}
refresh_token = data['refresh_token']
expires_at = Time.now + data['expires_in'].seconds
refresh_at = expires_at.to_time - 2.hours
opts = { data['access_token'] = result_data['access_token']
name: name data['refresh_token'] = result_data['refresh_token'] if result_data['refresh_token']
} data['token_type'] = result_data['token_type'] if result_data['token_type']
Jobs.enqueue_at(refresh_at, :refresh_api_access_token, opts) if result_data['expires_in']
data['token_expires_at'] = Time.now + result_data['expires_in'].seconds
data['token_refresh_at'] = data['token_expires_at'].to_time - 10.minutes
CustomWizard::Api::Authorization.set(name, opts = {
access_token: access_token, name: name
refresh_token: refresh_token, }
token_expires_at: expires_at,
token_refresh_at: refresh_at Jobs.enqueue_at(data['token_refresh_at'], :refresh_api_access_token, opts)
) end
CustomWizard::Api::Authorization.set(name, data)
end end
end end

Datei anzeigen

@ -74,6 +74,7 @@ after_initialize do
delete 'admin/wizards/apis/:name' => 'api#remove' delete 'admin/wizards/apis/:name' => 'api#remove'
delete 'admin/wizards/apis/logs/:name' => 'api#clearlogs' delete 'admin/wizards/apis/logs/:name' => 'api#clearlogs'
get 'admin/wizards/apis/:name/redirect' => 'api#redirect' get 'admin/wizards/apis/:name/redirect' => 'api#redirect'
get 'admin/wizards/apis/:name/authorize' => 'api#authorize'
end end
end end