big oauth improvements
This commit is contained in:
@@ -1,37 +1,55 @@
|
|||||||
<template lang='pug'>
|
<template lang='pug'>
|
||||||
//- el-card.mt-5
|
el-card.mt-5
|
||||||
//- div(slot='header')
|
h4(slot='header') <nuxt-link :to='"/"'><img src='/favicon.ico'/></nuxt-link> {{settings.title}} - {{$t('common.authorize')}}
|
||||||
//- h4 <img src='/favicon.ico'/> App authorization
|
div
|
||||||
div(v-if='client')
|
|
||||||
h5 <u>{{$auth.user.email}}</u>
|
h5 <u>{{$auth.user.email}}</u>
|
||||||
p External application <b>{{client.name}}</b> want following permission grants:
|
p External application <code>{{client.name}}</code> want following permission grants:
|
||||||
ul
|
ul
|
||||||
li(v-for="scope in $route.query.scope.split(' ')") {{scope}}
|
li(v-for="s in scope.split(' ')") {{s}}
|
||||||
span You will be redirected to <b>{{$route.query.redirect_uri}}</b>
|
span(v-if='redirect_uri!=="urn:ietf:wg:oauth:2.0:oob"') You will be redirected to <code>{{$route.query.redirect_uri}}</code>
|
||||||
el-row.mt-3(justify='center')
|
el-row.mt-3(justify='center')
|
||||||
el-col(:span='12' :offset='6' style='text-align: center')
|
el-col(:span='12' :offset='6' style='text-align: center')
|
||||||
a(:href='authorizeURL')
|
a(:href='authorizeURL')
|
||||||
el-button.mr-1(plain type='success') {{$t('common.authorize')}}
|
el-button.mr-1(plain type='success') {{$t('common.authorize')}}
|
||||||
a(to='/')
|
a(href='/')
|
||||||
el-button.mt-1(plain type='warning') {{$t('common.cancel')}}
|
el-button.mt-1(plain type='warning') {{$t('common.cancel')}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import { Message } from 'element-ui'
|
|
||||||
import get from 'lodash/get'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
layout: 'modal',
|
layout: 'modal',
|
||||||
name: 'Authorize',
|
name: 'Authorize',
|
||||||
middleware: ['auth'],
|
middleware: ['auth'],
|
||||||
async asyncData ({ $axios, query }) {
|
async asyncData ({ $axios, query, error, req }) {
|
||||||
|
const { client_id, redirect_uri, scope, response_type } = query
|
||||||
|
let err = ''
|
||||||
|
if (!client_id) {
|
||||||
|
err = 'client_id is missing'
|
||||||
|
}
|
||||||
|
if (!redirect_uri) {
|
||||||
|
err = 'redirect_uri is missing'
|
||||||
|
}
|
||||||
|
if (!scope || scope !== 'write') {
|
||||||
|
err = 'scope is missing or wrong'
|
||||||
|
}
|
||||||
|
if (!response_type || response_type !== 'code') {
|
||||||
|
err = 'response_type is missing or wrong'
|
||||||
|
}
|
||||||
|
|
||||||
// retrieve client validity
|
// retrieve client validity
|
||||||
try {
|
try {
|
||||||
const client = await $axios.$get(`/client/${query.client_id}`)
|
const client = await $axios.$get(`/client/${client_id}`)
|
||||||
return { client }
|
if (!client) {
|
||||||
|
err = 'client not found'
|
||||||
|
}
|
||||||
|
if (err) {
|
||||||
|
return error({ statusCode: 404, message: err })
|
||||||
|
}
|
||||||
|
return { client, redirect_uri, scope, response_type }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
error({ statusCode: 400, message: 'Something goes wrong with OAuth authorization' })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
p(v-html="$t('login.description')")
|
p(v-html="$t('login.description')")
|
||||||
div(v-loading='loading')
|
div(v-loading='loading')
|
||||||
|
|
||||||
el-input.mb-2(v-model='email' type='email' name='email' prefix-icon='el-icon-user'
|
el-input.mb-2(v-model='email' type='email' title='email' prefix-icon='el-icon-user'
|
||||||
:placeholder='$t("common.email")' autocomplete='email' ref='email')
|
:placeholder='$t("common.email")' autocomplete='email' ref='email')
|
||||||
|
|
||||||
el-input.mb-1(v-model='password' @keyup.enter.native="submit"
|
el-input.mb-1(v-model='password' @keyup.enter.native="submit"
|
||||||
prefix-icon='el-icon-lock' name='password'
|
prefix-icon='el-icon-lock' name='password'
|
||||||
type='password' :placeholder='$t("common.password")')
|
type='password' :placeholder='$t("common.password")')
|
||||||
|
|
||||||
@@ -41,6 +41,9 @@ export default {
|
|||||||
return !this.email || !this.password
|
return !this.email || !this.password
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted () {
|
||||||
|
this.$refs.email.focus()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['login']),
|
...mapActions(['login']),
|
||||||
close () {
|
close () {
|
||||||
@@ -63,8 +66,8 @@ export default {
|
|||||||
try {
|
try {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
await this.$auth.loginWith('local', { data: { email: this.email, password: this.password } })
|
await this.$auth.loginWith('local', { data: { email: this.email, password: this.password } })
|
||||||
const user = await this.$axios.$get('/auth/user')
|
// const user = await this.$axios.$get('/auth/user')
|
||||||
this.$auth.setUser(user)
|
// this.$auth.setUser(user)
|
||||||
this.loading = false
|
this.loading = false
|
||||||
Message({ message: this.$t('login.ok'), showClose: true, type: 'success' })
|
Message({ message: this.$t('login.ok'), showClose: true, type: 'success' })
|
||||||
this.close()
|
this.close()
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
el-input.mb-2(v-model='user.description' type="textarea" rows='3' :placeholder="$t('common.description')")
|
el-input.mb-2(v-model='user.description' type="textarea" rows='3' :placeholder="$t('common.description')")
|
||||||
|
|
||||||
span(slot='footer')
|
|
||||||
el-button(plain type="success" :disabled='disabled' @click='register') {{$t('common.send')}} <v-icon name='chevron-right'/>
|
el-button(plain type="success" :disabled='disabled' @click='register') {{$t('common.send')}} <v-icon name='chevron-right'/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
el-main
|
el-main#edit_page
|
||||||
h5.text-center {{edit?$t('common.edit_event'):$t('common.add_event')}}
|
h5.text-center {{edit?$t('common.edit_event'):$t('common.add_event')}}
|
||||||
el-form(v-loading='loading')
|
el-form(v-loading='loading')
|
||||||
|
|
||||||
@@ -18,10 +18,11 @@
|
|||||||
|
|
||||||
//- tags
|
//- tags
|
||||||
div {{$t('event.tag_description')}}
|
div {{$t('event.tag_description')}}
|
||||||
el-select.mb-3(v-model='event.tags' multiple filterable
|
client-only
|
||||||
@input.native='queryTags=$event.target.value' @change='queryTags=""'
|
el-select.mb-3(v-model='event.tags' multiple filterable
|
||||||
allow-create default-first-option placeholder='Tag')
|
@input.native='queryTags=$event.target.value' @change='queryTags=""'
|
||||||
el-option(v-for='tag in filteredTags' :key='tag' :label='tag' :value='tag')
|
allow-create default-first-option placeholder='Tag')
|
||||||
|
el-option(v-for='tag in filteredTags' :key='tag.tag' :label='tag.tag' :value='tag.tag')
|
||||||
|
|
||||||
//- WHERE
|
//- WHERE
|
||||||
el-divider
|
el-divider
|
||||||
@@ -69,8 +70,9 @@
|
|||||||
el-radio-button(v-for='whenPattern in whenPatterns' :label='whenPattern.key' :key='whenPatterns.key')
|
el-radio-button(v-for='whenPattern in whenPatterns' :label='whenPattern.key' :key='whenPatterns.key')
|
||||||
span {{whenPattern.label}}
|
span {{whenPattern.label}}
|
||||||
|
|
||||||
.text-center(inline)
|
//- form.el-form.text-center.inline.el-form-inline
|
||||||
el-form-item(:label="$t('event.from')")
|
.text-center
|
||||||
|
el-form-item(:label="$t('event.from')" width='100')
|
||||||
el-time-select.mr-2(ref='time_start'
|
el-time-select.mr-2(ref='time_start'
|
||||||
v-model="time.start"
|
v-model="time.start"
|
||||||
:picker-options="{ start: '00:00', step: '00:30', end: '24:00'}")
|
:picker-options="{ start: '00:00', step: '00:30', end: '24:00'}")
|
||||||
@@ -79,7 +81,6 @@
|
|||||||
:picker-options="{start: '00:00', step: '00:30', end: '24:00'}")
|
:picker-options="{start: '00:00', step: '00:30', end: '24:00'}")
|
||||||
|
|
||||||
List(v-if='event.type==="normal" && todayEvents.length' :events='todayEvents' :title='$t("event.same_day")')
|
List(v-if='event.type==="normal" && todayEvents.length' :events='todayEvents' :title='$t("event.same_day")')
|
||||||
//- el-button.float-right(@click='next' type='succes' :disabled='!couldProceed') {{$t('common.next')}}
|
|
||||||
|
|
||||||
//- MEDIA / FLYER / POSTER
|
//- MEDIA / FLYER / POSTER
|
||||||
el-divider <v-icon name='image'/> {{$t('common.media')}}
|
el-divider <v-icon name='image'/> {{$t('common.media')}}
|
||||||
@@ -114,18 +115,6 @@ export default {
|
|||||||
validate ({ store }) {
|
validate ({ store }) {
|
||||||
return (store.state.auth.loggedIn || store.state.settings.allow_anon_event)
|
return (store.state.auth.loggedIn || store.state.settings.allow_anon_event)
|
||||||
},
|
},
|
||||||
// fetch ({ store, $axios }) {
|
|
||||||
// try {
|
|
||||||
// const now = new Date()
|
|
||||||
// const events = await $axios.$get(`/event/${now.getMonth()}/${now.getFullYear()}`)
|
|
||||||
// store.commit('setEvents', events)
|
|
||||||
// const { tags, places } = await $axios.$get('/event/meta')
|
|
||||||
// store.commit('update', { tags, places })
|
|
||||||
// } catch (e) {
|
|
||||||
// console.error('Error ', e)
|
|
||||||
// }
|
|
||||||
// moment.locale(store.state.locale)
|
|
||||||
// },
|
|
||||||
async asyncData ({ params, $axios, error, store }) {
|
async asyncData ({ params, $axios, error, store }) {
|
||||||
if (params.edit) {
|
if (params.edit) {
|
||||||
const data = { time: {}, event: { place: {} } }
|
const data = { time: {}, event: { place: {} } }
|
||||||
@@ -267,8 +256,9 @@ export default {
|
|||||||
filteredTags () {
|
filteredTags () {
|
||||||
const queryTags = this.queryTags.toLowerCase()
|
const queryTags = this.queryTags.toLowerCase()
|
||||||
return _(this.tags)
|
return _(this.tags)
|
||||||
.filter(t => !this.event.tags.includes(t))
|
.filter(t => !this.event.tags.includes(t.tag))
|
||||||
.filter(t => t.includes(queryTags))
|
.filter(t => t.tag.includes(queryTags))
|
||||||
|
// .pick('tag')
|
||||||
.take(5)
|
.take(5)
|
||||||
.value()
|
.value()
|
||||||
},
|
},
|
||||||
@@ -418,6 +408,10 @@ i {
|
|||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#edit_page .el-form-item {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
.el-upload,
|
.el-upload,
|
||||||
.el-upload-dragger {
|
.el-upload-dragger {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -61,28 +61,29 @@ export default {
|
|||||||
*/
|
*/
|
||||||
</script>
|
</script>
|
||||||
<style lang='less'>
|
<style lang='less'>
|
||||||
.embed_event{
|
|
||||||
|
|
||||||
a {
|
|
||||||
transition: margin .1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
transform: prospective(10) translateX(10);
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.embed_event {
|
.embed_event {
|
||||||
|
transition: margin .1s;
|
||||||
background-image: url('/favicon.ico');
|
background-image: url('/favicon.ico');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position-x: right;
|
background-position-x: right;
|
||||||
background-position-y: bottom;
|
background-position-y: bottom;
|
||||||
img {
|
background-color: #1f1f1f;
|
||||||
width: 150px;
|
|
||||||
object-fit: cover;
|
display: inline-block;
|
||||||
object-position: top;
|
border: 1px solid #b1a3a3;
|
||||||
margin-right: 5px;
|
margin: 0px auto;
|
||||||
height: 100%;
|
padding: 0px;
|
||||||
|
width: 400px;
|
||||||
|
height: 210px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 10px;
|
||||||
|
// transition: all .2s;
|
||||||
|
margin: 0px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: prospective(10) translateX(10);
|
||||||
|
margin-left: 5px;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.event-info {
|
.event-info {
|
||||||
@@ -97,20 +98,13 @@ a:hover {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
background-color: #1f1f1f;
|
img {
|
||||||
display: inline-block;
|
width: 150px;
|
||||||
border: 1px solid #b1a3a3;
|
object-fit: cover;
|
||||||
margin: 0px auto;
|
object-position: top;
|
||||||
padding: 0px;
|
margin-right: 5px;
|
||||||
width: 400px;
|
height: 100%;
|
||||||
height: 210px;
|
}
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 10px;
|
|
||||||
// transition: all .2s;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
}
|
||||||
}
|
</style>
|
||||||
// .embed_event:hover {
|
|
||||||
// transform: scale(1.03);
|
|
||||||
// }
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -39,8 +39,7 @@
|
|||||||
el-menu-item(@click='showEmbed=true') <i class='el-icon-copy-document'></i> {{$t('common.embed')}}
|
el-menu-item(@click='showEmbed=true') <i class='el-icon-copy-document'></i> {{$t('common.embed')}}
|
||||||
|
|
||||||
//- TODO (ics of recurrent events)
|
//- TODO (ics of recurrent events)
|
||||||
//- el-menu-item(v-if='!event.recurrent')
|
el-menu-item(v-if='!event.recurrent')
|
||||||
el-menu-item
|
|
||||||
a(:href='`${settings.baseurl}/api/event/${event.id}.ics`') <i class='el-icon-date'></i> {{$t('common.add_to_calendar')}}
|
a(:href='`${settings.baseurl}/api/event/${event.id}.ics`') <i class='el-icon-date'></i> {{$t('common.add_to_calendar')}}
|
||||||
EventAdmin(v-if='is_mine' :event='event')
|
EventAdmin(v-if='is_mine' :event='event')
|
||||||
|
|
||||||
@@ -96,7 +95,7 @@ export default {
|
|||||||
: event.start_datetime
|
: event.start_datetime
|
||||||
// const now = new Date()
|
// const now = new Date()
|
||||||
// const events = await $axios.$get(
|
// const events = await $axios.$get(
|
||||||
// `/event/${now.getMonth()}/${now.getFullYear()}`
|
// `/event/${now.getMonth()}/${now.getFullYear()}`
|
||||||
// )
|
// )
|
||||||
// store.commit('setEvents', events)
|
// store.commit('setEvents', events)
|
||||||
return { event, id: Number(id) }
|
return { event, id: Number(id) }
|
||||||
@@ -281,7 +280,7 @@ export default {
|
|||||||
await this.$axios.post('/instances/toggle_user_block', { user_id: resource.apUserApId })
|
await this.$axios.post('/instances/toggle_user_block', { user_id: resource.apUserApId })
|
||||||
Message({ message: this.$t('admin.user_blocked', { user: resource.apUserApId }), type: 'success', showClose: true })
|
Message({ message: this.$t('admin.user_blocked', { user: resource.apUserApId }), type: 'success', showClose: true })
|
||||||
},
|
},
|
||||||
async deleteResource (resource) {
|
deleteResource (resource) {
|
||||||
MessageBox.confirm(this.$t('admin.delete_resource_confirm'),
|
MessageBox.confirm(this.$t('admin.delete_resource_confirm'),
|
||||||
this.$t('common.confirm'), {
|
this.$t('common.confirm'), {
|
||||||
confirmButtonText: this.$t('common.ok'),
|
confirmButtonText: this.$t('common.ok'),
|
||||||
|
|||||||
@@ -83,18 +83,18 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.filters.places.length) {
|
if (this.filters.places.length) {
|
||||||
params.push(`places=${this.filters.places}`)
|
params.push(`places=${this.filters.places.map(p => p.id)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.filters.tags.length) {
|
if (this.filters.tags.length) {
|
||||||
params.push(`tags=${this.filters.tags}`)
|
params.push(`tags=${this.filters.tags.map(t => t.id)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<iframe style='border: 0px; width: 100%;' src="${this.settings.baseurl}/embed/list?${params.join('&')}"></iframe>`
|
return `<iframe style='border: 0px; width: 100%;' src="${this.settings.baseurl}/embed/list?${params.join('&')}"></iframe>`
|
||||||
},
|
},
|
||||||
link () {
|
link () {
|
||||||
const tags = this.filters.tags.join(',')
|
const tags = this.filters.tags.map(t => t.id).join(',')
|
||||||
const places = this.filters.places.join(',')
|
const places = this.filters.places.map(p => p.id).join(',')
|
||||||
let query = ''
|
let query = ''
|
||||||
if (tags || places) {
|
if (tags || places) {
|
||||||
query = '?'
|
query = '?'
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Home from '~/components/Home.vue'
|
import Home from '~/components/Home.vue'
|
||||||
import Nav from '~/components/Nav.vue'
|
|
||||||
import moment from 'moment-timezone'
|
import moment from 'moment-timezone'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Index',
|
name: 'Index',
|
||||||
components: { Nav, Home },
|
components: { Home },
|
||||||
fetch ({ store }) {
|
fetch ({ store }) {
|
||||||
moment.tz.setDefault(store.state.settings.instance_timezone)
|
moment.tz.setDefault(store.state.settings.instance_timezone)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,57 +1,117 @@
|
|||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const { promisify } = require('util')
|
const { promisify } = require('util')
|
||||||
const randomBytes = promisify(crypto.randomBytes)
|
const randomBytes = promisify(crypto.randomBytes)
|
||||||
const { oauth_client: OAuthClient, oauth_token: OAuthToken,
|
const {
|
||||||
oauth_code: OAuthCode } = require('../models')
|
oauth_client: OAuthClient, oauth_token: OAuthToken,
|
||||||
|
oauth_code: OAuthCode, user: User
|
||||||
|
} = require('../models')
|
||||||
|
const debug = require('debug')('oauth')
|
||||||
|
|
||||||
async function randomString(len = 16) {
|
async function randomString (len = 16) {
|
||||||
const bytes = await randomBytes(len*8)
|
const bytes = await randomBytes(len * 8)
|
||||||
return crypto
|
return crypto
|
||||||
.createHash('sha1')
|
.createHash('sha1')
|
||||||
.update(bytes)
|
.update(bytes)
|
||||||
.digest('hex')
|
.digest('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const oauthController = {
|
const oauthController = {
|
||||||
|
|
||||||
async getClient (req, res) {
|
// create client => http:///gancio.org/oauth#create-client
|
||||||
const client_id = req.params.client_id
|
|
||||||
const client = await OAuthClient.findOne({ where: { client_id }})
|
|
||||||
console.error('ma non ho trovato il client ', client_id, client )
|
|
||||||
res.json(client)
|
|
||||||
},
|
|
||||||
|
|
||||||
async createClient (req, res) {
|
async createClient (req, res) {
|
||||||
|
debug('Create client ', req.body.client_name)
|
||||||
|
// only write scope is supported
|
||||||
|
if (req.body.scopes && req.body.scopes !== 'write') {
|
||||||
|
return res.status(422).json({ error: 'Invalid scopes' })
|
||||||
|
}
|
||||||
|
|
||||||
const client = {
|
const client = {
|
||||||
|
id: await randomString(256),
|
||||||
name: req.body.client_name,
|
name: req.body.client_name,
|
||||||
redirectUris: req.body.redirect_uris || 'urn:ietf:wg:oauth:2.0:oob',
|
redirectUris: req.body.redirect_uris,
|
||||||
scopes: req.body.scopes || 'write',
|
scopes: req.body.scopes || 'write',
|
||||||
client_id: await randomString(256),
|
website: req.body.website,
|
||||||
client_secret: await randomString(256)
|
client_secret: await randomString(256)
|
||||||
}
|
}
|
||||||
res.json(await OAuthClient.create(client))
|
|
||||||
|
try {
|
||||||
|
await OAuthClient.create(client)
|
||||||
|
client.client_id = client.id
|
||||||
|
delete client.id
|
||||||
|
res.json(client)
|
||||||
|
} catch (e) {
|
||||||
|
debug(e)
|
||||||
|
res.status(400).json(e)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async associate (req, res) {
|
async getClients (req, res) {
|
||||||
const { client_id, redirect_uri, response_type } = req.query
|
const tokens = await OAuthToken.findAll({
|
||||||
console.error('dentro associate ', client_id, redirect_uri, response_type )
|
include: [{ model: User, where: { id: req.user.id } }, { model: OAuthClient, as: 'client' }],
|
||||||
|
raw: true,
|
||||||
|
nest: true
|
||||||
|
})
|
||||||
|
res.json(tokens)
|
||||||
},
|
},
|
||||||
|
|
||||||
model: {
|
model: {
|
||||||
async getClient (clientId, clientSecret) {
|
|
||||||
console.error(`model getClient ${clientId} / ${clientSecret}`)
|
/**
|
||||||
const client = await OAuthClient.findByPk(clientId)
|
* Invoked to retrieve an existing access token previously saved through #saveToken().
|
||||||
client.grants = ['authorization_code']
|
* https://oauth2-server.readthedocs.io/en/latest/model/spec.html#getaccesstoken-accesstoken-callback
|
||||||
return client || false
|
* */
|
||||||
|
async getAccessToken (accessToken) {
|
||||||
|
const oauth_token = await OAuthToken.findByPk(accessToken,
|
||||||
|
{ include: [User, { model: OAuthClient, as: 'client' }], nest: true, raw: true })
|
||||||
|
return oauth_token
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveAuthorizationCode(code, client, user) {
|
/**
|
||||||
console.error('dentro save auth code ', client, user, code)
|
* Invoked to retrieve a client using a client id or a client id/client secret combination, depending on the grant type.
|
||||||
|
*/
|
||||||
|
async getClient (client_id, client_secret) {
|
||||||
|
const client = await OAuthClient.findByPk(client_id, { raw: true })
|
||||||
|
if (client_secret && client_secret !== client.client_secret) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client) { client.grants = ['authorization_code'] }
|
||||||
|
|
||||||
|
return client
|
||||||
|
},
|
||||||
|
|
||||||
|
async getRefreshToken (refresh_token) {
|
||||||
|
const oauth_token = await OAuthToken.findOne({ where: { refresh_token }, raw: true })
|
||||||
|
return oauth_token
|
||||||
|
},
|
||||||
|
|
||||||
|
async getAuthorizationCode (code) {
|
||||||
|
const oauth_code = await OAuthCode.findByPk(code,
|
||||||
|
{ include: [User, { type: OAuthClient, as: 'client' }], nest: true, raw: true })
|
||||||
|
return oauth_code
|
||||||
|
},
|
||||||
|
|
||||||
|
async saveToken (token, client, user) {
|
||||||
|
token.userId = user.id
|
||||||
|
token.oauthClientId = client.id
|
||||||
|
const oauth_token = await OAuthToken.create(token)
|
||||||
|
oauth_token.client = client
|
||||||
|
oauth_token.user = user
|
||||||
|
return oauth_token
|
||||||
|
},
|
||||||
|
|
||||||
|
async revokeAuthorizationCode (code) {
|
||||||
|
const oauth_code = await OAuthCode.findByPk(code)
|
||||||
|
return oauth_code.destroy()
|
||||||
|
},
|
||||||
|
|
||||||
|
async saveAuthorizationCode (code, client, user) {
|
||||||
|
code.userId = user.id
|
||||||
|
code.oauthClientId = client.id
|
||||||
const ret = await OAuthCode.create(code)
|
const ret = await OAuthCode.create(code)
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const instanceController = require('./controller/instance')
|
|||||||
const apUserController = require('./controller/ap_user')
|
const apUserController = require('./controller/ap_user')
|
||||||
const resourceController = require('./controller/resource')
|
const resourceController = require('./controller/resource')
|
||||||
const oauthController = require('./controller/oauth')
|
const oauthController = require('./controller/oauth')
|
||||||
|
const oauth = require('./oauth')
|
||||||
|
|
||||||
const storage = require('./storage')
|
const storage = require('./storage')
|
||||||
const upload = multer({ storage })
|
const upload = multer({ storage })
|
||||||
@@ -82,7 +83,7 @@ api.get('/event/:event_id.:format?', cors, eventController.get)
|
|||||||
api.get('/export/:type', cors, exportController.export)
|
api.get('/export/:type', cors, exportController.export)
|
||||||
|
|
||||||
// get events in this range
|
// get events in this range
|
||||||
api.get('/event/:month/:year', cors, eventController.getAll)
|
// api.get('/event/:month/:year', cors, eventController.getAll)
|
||||||
api.get('/event', cors, eventController.select)
|
api.get('/event', cors, eventController.select)
|
||||||
|
|
||||||
api.get('/instances', isAdmin, instanceController.getAll)
|
api.get('/instances', isAdmin, instanceController.getAll)
|
||||||
@@ -93,9 +94,12 @@ api.put('/resources/:resource_id', isAdmin, resourceController.hide)
|
|||||||
api.delete('/resources/:resource_id', isAdmin, resourceController.remove)
|
api.delete('/resources/:resource_id', isAdmin, resourceController.remove)
|
||||||
api.get('/resources', isAdmin, resourceController.getAll)
|
api.get('/resources', isAdmin, resourceController.getAll)
|
||||||
|
|
||||||
api.get('/client/:client_id', isAuth, oauthController.getClient)
|
api.get('/clients', isAuth, oauthController.getClients)
|
||||||
api.post('/client', oauthController.createClient)
|
api.post('/client', oauthController.createClient)
|
||||||
|
|
||||||
|
// api.get('/verify', oauth.oauthServer.authenticate(), (req, res) => {
|
||||||
|
// })
|
||||||
|
|
||||||
// Handle 404
|
// Handle 404
|
||||||
api.use((req, res) => {
|
api.use((req, res) => {
|
||||||
debug('404 Page not found: %s', req.path)
|
debug('404 Page not found: %s', req.path)
|
||||||
@@ -104,7 +108,7 @@ api.use((req, res) => {
|
|||||||
|
|
||||||
// Handle 500
|
// Handle 500
|
||||||
api.use((error, req, res, next) => {
|
api.use((error, req, res, next) => {
|
||||||
debug(error)
|
debug(error.toString())
|
||||||
res.status(500).send('500: Internal Server Error')
|
res.status(500).send('500: Internal Server Error')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
|
|
||||||
module.exports = (sequelize, DataTypes) => {
|
module.exports = (sequelize, DataTypes) => {
|
||||||
const OAuthClient = sequelize.define('oauth_client', {
|
const OAuthClient = sequelize.define('oauth_client', {
|
||||||
client_id: {
|
id: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
|
allowNull: false
|
||||||
},
|
},
|
||||||
name: DataTypes.STRING,
|
name: DataTypes.STRING,
|
||||||
scopes: DataTypes.STRING,
|
|
||||||
client_secret: DataTypes.STRING,
|
client_secret: DataTypes.STRING,
|
||||||
redirectUris: DataTypes.STRING
|
scopes: DataTypes.STRING,
|
||||||
|
redirectUris: DataTypes.STRING,
|
||||||
|
website: DataTypes.STRING
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
OAuthClient.associate = function (models) {
|
|
||||||
OAuthClient.belongsTo(models.user)
|
|
||||||
}
|
|
||||||
|
|
||||||
return OAuthClient
|
return OAuthClient
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ module.exports = (sequelize, DataTypes) => {
|
|||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
primaryKey: true
|
primaryKey: true
|
||||||
},
|
},
|
||||||
|
expiresAt: DataTypes.DATE,
|
||||||
scope: DataTypes.STRING,
|
scope: DataTypes.STRING,
|
||||||
redirect_uri: DataTypes.STRING
|
redirect_uri: DataTypes.STRING
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
OAuthCode.associate = function (models) {
|
OAuthCode.associate = function (models) {
|
||||||
OAuthCode.belongsTo(models.user)
|
OAuthCode.belongsTo(models.user)
|
||||||
OAuthCode.belongsTo(models.oauth_client)
|
OAuthCode.belongsTo(models.oauth_client, { as: 'client' })
|
||||||
}
|
}
|
||||||
|
|
||||||
return OAuthCode
|
return OAuthCode
|
||||||
|
|||||||
@@ -1,14 +1,30 @@
|
|||||||
|
|
||||||
module.exports = (sequelize, DataTypes) => {
|
module.exports = (sequelize, DataTypes) => {
|
||||||
const OAuthToken = sequelize.define('oauth_token', {
|
const OAuthToken = sequelize.define('oauth_token', {
|
||||||
access_token: DataTypes.STRING,
|
accessToken: {
|
||||||
refresh_token: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
scope: DataTypes.STRING,
|
allowNull: false,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
accessTokenExpiresAt: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
get () {
|
||||||
|
return new Date(this.getDataValue('accesTokenExpiresAt'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshToken: DataTypes.STRING,
|
||||||
|
refreshTokenExpiresAt: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
get () {
|
||||||
|
return new Date(this.getDataValue('accesTokenExpiresAt'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scope: DataTypes.STRING
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
OAuthToken.associate = function (models) {
|
OAuthToken.associate = function (models) {
|
||||||
OAuthToken.belongsTo(models.user)
|
OAuthToken.belongsTo(models.user)
|
||||||
OAuthToken.belongsTo(models.oauth_client)
|
OAuthToken.belongsTo(models.oauth_client, { as: 'client' })
|
||||||
}
|
}
|
||||||
|
|
||||||
return OAuthToken
|
return OAuthToken
|
||||||
|
|||||||
@@ -2,41 +2,36 @@ const express = require('express')
|
|||||||
const OAuthServer = require('express-oauth-server')
|
const OAuthServer = require('express-oauth-server')
|
||||||
const oauth = express.Router()
|
const oauth = express.Router()
|
||||||
const oauthController = require('./controller/oauth')
|
const oauthController = require('./controller/oauth')
|
||||||
|
const debug = require('debug')('oauth')
|
||||||
|
|
||||||
const oauthServer = new OAuthServer({
|
const oauthServer = new OAuthServer({
|
||||||
model: oauthController.model,
|
model: oauthController.model,
|
||||||
|
allowEmptyState: true,
|
||||||
useErrorHandler: true,
|
useErrorHandler: true,
|
||||||
|
continueMiddleware: false,
|
||||||
debug: true,
|
debug: true,
|
||||||
authenticateHandler: { handle(req) { return req.user } }
|
authenticateHandler: {
|
||||||
|
handle (req) {
|
||||||
|
if (!req.user) {
|
||||||
|
throw new Error('Not authenticated!')
|
||||||
|
}
|
||||||
|
return req.user
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
oauth.oauth = oauthServer
|
oauth.oauthServer = oauthServer
|
||||||
oauth.use(express.urlencoded({ extended: false }))
|
|
||||||
oauth.use(express.json())
|
oauth.use(express.json())
|
||||||
|
oauth.use(express.urlencoded({ extended: false }))
|
||||||
|
|
||||||
// post token
|
|
||||||
oauth.post('/token', oauthServer.token())
|
oauth.post('/token', oauthServer.token())
|
||||||
|
|
||||||
oauth.get('/authorize', async (req, res, next) => {
|
oauth.get('/authorize', oauthServer.authorize())
|
||||||
if (!req.user) {
|
|
||||||
return res.redirect(`/login?redirect=${req.path}&client_id=${req.query.client_id}&redirect_uri=${req.query.redirect_uri}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return oauthServer.authorize()
|
|
||||||
})
|
|
||||||
|
|
||||||
oauth.post('/authorize', (req, res, next) => {
|
|
||||||
if (!req.user) {
|
|
||||||
return res.redirect(`/login?redirect=${req.path}&client_id=${req.query.client_id}&redirect_uri=${req.query.redirect_uri}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return oauthServer.authorize()
|
|
||||||
})
|
|
||||||
|
|
||||||
oauth.use((err, req, res, next) => {
|
oauth.use((err, req, res, next) => {
|
||||||
res.status(500).json(err)
|
const error_msg = err.toString()
|
||||||
|
debug(err)
|
||||||
|
res.status(500).send(error_msg)
|
||||||
})
|
})
|
||||||
|
|
||||||
// oauth.post('/login', )
|
module.exports = oauth
|
||||||
|
|
||||||
module.exports = oauth
|
|
||||||
|
|||||||
@@ -42,11 +42,9 @@ module.exports = {
|
|||||||
moment.tz.setDefault(req.settings.instance_timezone)
|
moment.tz.setDefault(req.settings.instance_timezone)
|
||||||
|
|
||||||
// TODO: oauth
|
// TODO: oauth
|
||||||
// auth
|
|
||||||
jwt(req, res, async () => {
|
jwt(req, res, async () => {
|
||||||
if (!req.user) { return next() }
|
if (!req.user) { return next() }
|
||||||
req.user = await User.findOne({
|
req.user = await User.findOne({ where: { id: req.user.id, is_active: true } })
|
||||||
where: { id: req.user.id, is_active: true } })
|
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,25 +2,18 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
up: (queryInterface, Sequelize) => {
|
up: (queryInterface, Sequelize) => {
|
||||||
return queryInterface.createTable('oauth_clients', {
|
return queryInterface.createTable('oauth_clients', {
|
||||||
client_id: {
|
id: {
|
||||||
type: Sequelize.STRING,
|
allowNull: false,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
|
type: Sequelize.STRING
|
||||||
},
|
},
|
||||||
name: Sequelize.STRING,
|
name: Sequelize.STRING,
|
||||||
scopes: Sequelize.STRING,
|
|
||||||
client_secret: Sequelize.STRING,
|
client_secret: Sequelize.STRING,
|
||||||
|
scopes: Sequelize.STRING,
|
||||||
redirectUris: Sequelize.STRING,
|
redirectUris: Sequelize.STRING,
|
||||||
|
website: Sequelize.STRING,
|
||||||
createdAt: { type: Sequelize.DATE, allowNull: false },
|
createdAt: { type: Sequelize.DATE, allowNull: false },
|
||||||
updatedAt: { type: Sequelize.DATE, allowNull: false },
|
updatedAt: { type: Sequelize.DATE, allowNull: false }
|
||||||
userId: {
|
|
||||||
type: Sequelize.INTEGER,
|
|
||||||
references: {
|
|
||||||
model: 'users',
|
|
||||||
key: 'id'
|
|
||||||
},
|
|
||||||
onUpdate: 'CASCADE',
|
|
||||||
onDelete: 'CASCADE'
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -6,19 +6,20 @@ module.exports = {
|
|||||||
type: Sequelize.STRING,
|
type: Sequelize.STRING,
|
||||||
primaryKey: true
|
primaryKey: true
|
||||||
},
|
},
|
||||||
|
expiresAt: Sequelize.DATE,
|
||||||
scope: Sequelize.STRING,
|
scope: Sequelize.STRING,
|
||||||
redirect_uri: Sequelize.STRING,
|
redirect_uri: Sequelize.STRING,
|
||||||
createdAt: { type: Sequelize.DATE, allowNull: false },
|
createdAt: { type: Sequelize.DATE, allowNull: false },
|
||||||
updatedAt: { type: Sequelize.DATE, allowNull: false },
|
updatedAt: { type: Sequelize.DATE, allowNull: false },
|
||||||
oauthClientClientId: {
|
clientId: {
|
||||||
type: Sequelize.INTEGER,
|
type: Sequelize.STRING,
|
||||||
references: {
|
references: {
|
||||||
model: 'oauth_clients',
|
model: 'oauth_clients',
|
||||||
key: 'client_id'
|
key: 'id'
|
||||||
},
|
},
|
||||||
onUpdate: 'CASCADE',
|
onUpdate: 'CASCADE',
|
||||||
onDelete: 'CASCADE'
|
onDelete: 'CASCADE'
|
||||||
},
|
},
|
||||||
userId: {
|
userId: {
|
||||||
type: Sequelize.INTEGER,
|
type: Sequelize.INTEGER,
|
||||||
references: {
|
references: {
|
||||||
@@ -27,7 +28,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
onUpdate: 'CASCADE',
|
onUpdate: 'CASCADE',
|
||||||
onDelete: 'CASCADE'
|
onDelete: 'CASCADE'
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
45
server/migrations/20200119003226-oauth_token.js
Normal file
45
server/migrations/20200119003226-oauth_token.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
module.exports = {
|
||||||
|
up: (queryInterface, Sequelize) => {
|
||||||
|
return queryInterface.createTable('oauth_tokens', {
|
||||||
|
accessToken: {
|
||||||
|
allowNull: false,
|
||||||
|
primaryKey: true,
|
||||||
|
type: Sequelize.STRING
|
||||||
|
},
|
||||||
|
accessTokenExpiresAt: Sequelize.DATE,
|
||||||
|
refreshToken: Sequelize.STRING,
|
||||||
|
refreshTokenExpiresAt: Sequelize.DATE,
|
||||||
|
scope: Sequelize.STRING,
|
||||||
|
createdAt: { type: Sequelize.DATE, allowNull: false },
|
||||||
|
updatedAt: { type: Sequelize.DATE, allowNull: false },
|
||||||
|
clientId: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
references: {
|
||||||
|
model: 'oauth_clients',
|
||||||
|
key: 'id'
|
||||||
|
},
|
||||||
|
onUpdate: 'CASCADE',
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: Sequelize.INTEGER,
|
||||||
|
references: {
|
||||||
|
model: 'users',
|
||||||
|
key: 'id'
|
||||||
|
},
|
||||||
|
onUpdate: 'CASCADE',
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface, Sequelize) => {
|
||||||
|
/*
|
||||||
|
Add reverting commands here.
|
||||||
|
Return a promise to correctly handle asynchronicity.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
*/
|
||||||
|
return queryInterface.dropTable('oauth_tokens')
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user