Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev 1.2.0 #17

Merged
merged 12 commits into from
Apr 18, 2024
10 changes: 8 additions & 2 deletions extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
use Flarum\Extend;
use Flarum\Http\Middleware\AuthenticateWithHeader;
use Flarum\Http\Middleware\CheckCsrfToken;
use FoskyM\OAuthCenter\Middlewares\ResourceScopeMiddleware;
use Flarum\Http\Middleware\ParseJsonBody;
use FoskyM\OAuthCenter\Middlewares\ResourceScopeAuthMiddleware;
use FoskyM\OAuthCenter\Middlewares\ResourceScopeFieldsMiddleware;
use FoskyM\OAuthCenter\Middlewares\UnsetCsrfMiddleware;
use FoskyM\OAuthCenter\Middlewares\UserCredentialsMiddleware;

Expand Down Expand Up @@ -46,6 +48,9 @@
->patch('/oauth-scopes/{id}', 'oauth.scopes.update', Api\Controller\UpdateScopeController::class)
->delete('/oauth-scopes/{id}', 'oauth.scopes.delete', Api\Controller\DeleteScopeController::class)

->post('/oauth-tokens', 'oauth.tokens.create', Api\Controller\CreateTokenController::class)
->delete('/oauth-tokens/expired', 'oauth.tokens.delete.expired', Api\Controller\DeleteExpiredTokenController::class)

->get('/oauth-records', 'oauth.records.list', Api\Controller\ListRecordController::class)

->get('/user', 'user.show', Controllers\ApiUserController::class),
Expand All @@ -58,7 +63,8 @@
->serializeToForum('foskym-oauth-center.authorization_method_fetch', 'foskym-oauth-center.authorization_method_fetch', 'boolval'),

(new Extend\Middleware('api'))
->insertAfter(AuthenticateWithHeader::class, ResourceScopeMiddleware::class),
->insertAfter(AuthenticateWithHeader::class, ResourceScopeAuthMiddleware::class)
->add(ResourceScopeFieldsMiddleware::class),
(new Extend\Middleware('forum'))
->insertBefore(CheckCsrfToken::class, UnsetCsrfMiddleware::class)
->insertAfter(CheckCsrfToken::class, UserCredentialsMiddleware::class),
Expand Down
2 changes: 1 addition & 1 deletion js/dist/admin.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/dist/admin.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/dist/forum.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/dist/forum.js.map

Large diffs are not rendered by default.

70 changes: 70 additions & 0 deletions js/src/admin/components/AddTokenModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import app from 'flarum/admin/app';
import Modal from 'flarum/common/components/Modal';
import Button from 'flarum/common/components/Button';
import Stream from 'flarum/common/utils/Stream';
export default class AddTokenModal extends Modal {
oninit(vnode) {
super.oninit(vnode);

this.fields = [
'access_token',
'client_id',
'user_id',
'expires',
'scope'
];

this.values = this.fields.reduce((values, key) => {
values[key] = Stream('');
return values;
}, {});
}

className() {
return 'AddTokenModal';
}

title() {
return app.translator.trans('foskym-oauth-center.admin.tokens.add_token');
}

content() {
return (
<div className="Modal-body">
<form onsubmit={this.onsubmit.bind(this)}>
{this.fields.map(key =>
<div className="Form-group">
<label>{key}</label>
<input className="FormControl" bidi={this.values[key]} required="required"/>
</div>
)}
<div className="Form-group">
{Button.component({
type: 'submit',
className: 'Button Button--primary Button--block EditClientModal-save',
loading: this.loading,
}, app.translator.trans('core.admin.settings.submit_button'))}
</div>
</form>
</div>
);
}

onsubmit(e) {
e.preventDefault();
this.loading = true;

const token = app.store.createRecord('oauth-tokens');

const tokenData = this.fields.reduce((data, key) => {
data[key] = this.values[key]();
return data;
}, {});

token.save(tokenData).then(() => {
this.loading = false;
m.redraw();
this.hide();
});
}
}
17 changes: 12 additions & 5 deletions js/src/admin/components/EditScopeModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export default class EditScopeModal extends Modal {
'is_default',
'scope_name',
'scope_icon',
'scope_desc'
'scope_desc',

'visible_fields',
];

this.values = this.fields.reduce((values, key) => {
Expand All @@ -43,10 +45,17 @@ export default class EditScopeModal extends Modal {
{this.renderFormGroups(this.fields.slice(0, 4))}
</div>
<div className="OAuthCenter-Column">
{this.renderFormGroups(this.fields.slice(4, 8))}
{this.renderFormGroups(this.fields.slice(4, 7))}
</div>
</div>

<div className="Form-group">
<label>{app.translator.trans('foskym-oauth-center.admin.scopes.visible_fields')}</label>
<textarea className="FormControl" bidi={this.values['visible_fields']} rows={3} placeholder={"id,username,email,data.attributes"}>
{this.values.visible_fields()}
</textarea>
</div>

<div className="Form-group">
{Button.component({
type: 'submit',
Expand Down Expand Up @@ -89,14 +98,12 @@ export default class EditScopeModal extends Modal {
'PATCH': 'PATCH',
},
value: this.values[key](),
disabled: this.scope.resource_path() === '/api/user' && key === 'method',
onchange: this.values[key],
}) : key === 'is_default' ? Checkbox.component({
className: 'OAuthCenter-Checkbox',
state: this.values[key]() === 1 || false,
disabled: this.scope.resource_path() === '/api/user' && key === 'is_default',
onchange: (checked) => this.values[key](checked ? 1 : 0),
}) : <input className="FormControl" bidi={this.values[key]} disabled={this.scope.resource_path() === '/api/user' && key === 'resource_path'}/>}
}) : <input className="FormControl" bidi={this.values[key]}/>}
</div>
)
}
Expand Down
21 changes: 10 additions & 11 deletions js/src/admin/components/SettingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ import Button from 'flarum/common/components/Button';
import IndexPage from "../pages/IndexPage";
import ClientsPage from "../pages/ClientsPage";
import ScopesPage from "../pages/ScopesPage";
import TokensPage from "../pages/TokensPage";

export default class SettingsPage extends ExtensionPage {
translationPrefix = 'foskym-oauth-center.admin.page.';
pages = {
index: IndexPage,
clients: ClientsPage,
scopes: ScopesPage
scopes: ScopesPage,
tokens: TokensPage
};
icons = {
index: 'home',
clients: 'network-wired',
scopes: 'user-lock',
tokens: 'key',
};

content() {
Expand Down Expand Up @@ -48,16 +56,7 @@ export default class SettingsPage extends ExtensionPage {
}

iconForPage(page) {
switch (page) {
case 'index':
return 'fas fa-home';
case 'clients':
return 'fas fa-network-wired';
case 'scopes':
return 'fas fa-user-lock';
default:
return '';
}
return `fas fa-${this.icons[page]}` || '';
}

pageContent(page) {
Expand Down
1 change: 1 addition & 0 deletions js/src/admin/pages/IndexPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default class IndexPage extends Page {
Select.component({
options: {
'box': 'Box',
'card': 'Card',
'column': 'Column'
},
value: this.values['foskym-oauth-center.' + this.fields[0]],
Expand Down
2 changes: 1 addition & 1 deletion js/src/admin/pages/ScopesPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default class ScopesPage extends Page {
icon: 'fas fa-edit',
onclick: () => this.showEditModal(scope),
}),
scope.resource_path() !== '/api/user' && Button.component({
Button.component({
className: 'Button Button--icon',
icon: 'fas fa-times',
onclick: () => {
Expand Down
61 changes: 61 additions & 0 deletions js/src/admin/pages/TokensPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import app from 'flarum/admin/app';
import Page from 'flarum/common/components/Page';
import Button from 'flarum/common/components/Button';
import Alert from 'flarum/common/components/Alert';
import AddTokenModal from '../components/AddTokenModal';

export default class TokensPage extends Page {
translationPrefix = 'foskym-oauth-center.admin.tokens.';
scopes = [];

oninit(vnode) {
super.oninit(vnode);

this.fields = [];

// app.store.find('oauth-tokens').then(r => {
// this.scopes = r;
// m.redraw();
// });
}

view() {
return (
<div class={"OAuthCenter-scopesPage"}>
{[
Button.component({
type: 'button',
className: 'Button',
onclick: () => {
this.showAddModal();
}
}, app.translator.trans('foskym-oauth-center.admin.tokens.add_token')),
Button.component({
type: 'button',
className: 'Button',
onclick: () => {
this.deleteExpiredTokens();
}
}, app.translator.trans('foskym-oauth-center.admin.tokens.delete_token'))
]}
</div>
);
}

deleteExpiredTokens() {
app.request({
method: 'DELETE',
url: '/api/oauth-tokens/expired'
}).then(() => {
app.alerts.show(
Alert,
{type: 'success'},
'success!'
)
});
}

showAddModal() {
app.modal.show(AddTokenModal, {});
}
}
4 changes: 3 additions & 1 deletion js/src/common/extend.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import Extend from 'flarum/common/extenders';
import Client from "./models/Client";
import Scope from "./models/Scope";
import Record from "./models/Record";
import Token from "./models/Token";

export default [
new Extend.Store()
.add('oauth-clients', Client)
.add('oauth-scopes', Scope)
.add('oauth-records', Record),
.add('oauth-records', Record)
.add('oauth-tokens', Token)
];
1 change: 1 addition & 0 deletions js/src/common/models/Scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default class Scope extends Model {
scope = Model.attribute('scope');
resource_path = Model.attribute('resource_path');
method = Model.attribute('method');
visible_fields = Model.attribute('visible_fields');
is_default = Model.attribute('is_default');
scope_name = Model.attribute('scope_name');
scope_icon = Model.attribute('scope_icon');
Expand Down
9 changes: 9 additions & 0 deletions js/src/common/models/Token.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Model from 'flarum/common/Model';

export default class Token extends Model {
access_token = Model.attribute('access_token');
client_id = Model.attribute('client_id');
user_id = Model.attribute('user_id');
expires = Model.attribute('expires', Model.transformDate);
scope = Model.attribute('scope');
}
2 changes: 1 addition & 1 deletion js/src/forum/components/ScopeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default class ScopeComponent extends Component {
{
(scope.scope_icon().indexOf('fa-') > -1) ?
<i class={"oauth-scope-object fa-2x " + scope.scope_icon()}
style="margin-left:2px;color:#000"></i> :
style="margin-left:2px;"></i> :
<img class="oauth-scope-object" src={scope.scope_icon()} style="width:32px"/>
}
</div>
Expand Down
46 changes: 20 additions & 26 deletions js/src/forum/components/oauth/AuthorizePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ export default class AuthorizePage extends IndexPage {
app.store.find('oauth-clients', params.client_id),
app.store.find('oauth-scopes')
]).then(([client, scopes]) => {
if (client.length === 0) {
if (!client) {
m.route.set('/');
return;
}

this.client = client[0];
this.client = client;
this.scopes = scopes;

let uris = this.client.redirect_uri().split(' ');
Expand Down Expand Up @@ -96,23 +96,26 @@ export default class AuthorizePage extends IndexPage {
<div class="oauth-header">
<h2>{app.forum.attribute('title')}</h2>
<p>
{app.translator.trans('foskym-oauth-center.forum.authorize.access')} <a
href={this.client.client_home()} target="_blank">{this.client.client_name()}</a>
{app.translator.trans('foskym-oauth-center.forum.authorize.access')} <Tooltip text={this.client.client_desc()} position="bottom">
<a href={this.client.client_home()} target="_blank">{this.client.client_name()}</a>
</Tooltip>
</p>

</div>
<div class="oauth-body">

{/*<div class="oauth-user">*/}
{/* {avatar(app.session.user, {className: 'oauth-avatar'})}*/}
{/* <div class="oauth-username">*/}
{/* <b>{app.session.user.username()}</b>*/}
{/* <span>{app.session.user.displayName()}</span>*/}
{/* </div>*/}
{/*</div>*/}
<div class="oauth-user">
{avatar(app.session.user, {className: 'oauth-avatar'})}
<div class="oauth-username">
<b>{app.session.user.username()}</b>
<span>{app.session.user.displayName()}</span>
</div>
</div>

<div class="oauth-info">
<img src={app.forum.attribute('faviconUrl')} alt="favicon"/>
<Tooltip text={app.forum.attribute('title')}>
<img src={app.forum.attribute('faviconUrl')} alt="favicon"/>
</Tooltip>
<i class="fas fa-exchange-alt fa-2x"></i>
<Tooltip text={this.client.client_desc()}>
<img src={this.client.client_icon()} alt="client_icon"/>
Expand All @@ -126,19 +129,14 @@ export default class AuthorizePage extends IndexPage {
.filter(scope => scope)
.map(scope => {
let scope_info = this.scopes.find(s => s.scope() === scope);
if (scope_info == null) {
return '';
}
return <ScopeComponent scope={scope_info} client={this.client} />;
return scope_info && <ScopeComponent scope={scope_info} client={this.client} />;
})
}
</div>
<form class="oauth-form" method="post" id="form" action="/oauth/authorize" onsubmit={this.onsubmit.bind(this)}>
<input type="hidden" name="response_type" value={this.params.response_type}/>
<input type="hidden" name="client_id" value={this.params.client_id}/>
<input type="hidden" name="redirect_uri" value={this.params.redirect_uri}/>
<input type="hidden" name="state" value={this.params.state}/>
<input type="hidden" name="scope" value={this.params.scope}/>
{Object.keys(this.params).map(key => (
<input type="hidden" name={key} value={this.params[key]} />
))}
<input type="hidden" name="is_authorized" value={this.is_authorized}/>
<div class="oauth-form-item oauth-btn-group">
<Button className="Button" type="submit" style="width: 50%;" onclick={this.deny.bind(this)}
Expand Down Expand Up @@ -173,11 +171,7 @@ export default class AuthorizePage extends IndexPage {
method: 'POST',
url: '/oauth/authorize/fetch',
body: {
response_type: this.params.response_type,
client_id: this.params.client_id,
redirect_uri: this.params.redirect_uri,
state: this.params.state,
scope: this.params.scope,
...this.params,
is_authorized: this.is_authorized,
}
}).then((params) => {
Expand Down