Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-09 20:02:54 +01:00
distinguish between 2 legged and 3 legged oauth && other authorization improvements
Dieser Commit ist enthalten in:
Ursprung
4f195c704a
Commit
b9f8cc61b2
6 geänderte Dateien mit 147 neuen und 93 gelöschten Zeilen
|
@ -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) {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Laden …
In neuem Issue referenzieren