squash new oauth2 flow
This commit is contained in:
parent
19ca1b0283
commit
44e9533828
24 changed files with 935 additions and 427 deletions
|
@ -1,12 +1,14 @@
|
|||
All notable changes to this project will be documented in this file.
|
||||
|
||||
### UNRELEASED
|
||||
- new plugin system
|
||||
- new plugin system - fix #17
|
||||
- new "publish on telegram" plugin: thanks @fadelkon
|
||||
- i18n refactoring
|
||||
- people can now choose the language displayed - fix #171
|
||||
- fix place "[Object]" issue - #194
|
||||
- admin could choose a custom fallback image - fix #195
|
||||
- it is now possible NOT to enter the end time of an event - fix #188
|
||||
|
||||
|
||||
|
||||
### 1.5.6 - 22 set '22
|
||||
|
|
|
@ -125,7 +125,7 @@ module.exports = {
|
|||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
},
|
||||
logout: false,
|
||||
user: { url: '/user', method: 'get', propertyName: false }
|
||||
user: { url: '/user', method: 'get', propertyName: false, autoFetch: false }
|
||||
},
|
||||
tokenRequired: true,
|
||||
tokenType: 'Bearer'
|
||||
|
|
11
package.json
11
package.json
|
@ -47,7 +47,7 @@
|
|||
"dompurify": "^2.3.10",
|
||||
"email-templates": "^10.0.1",
|
||||
"express": "^4.18.1",
|
||||
"express-oauth-server": "lesion/express-oauth-server#master",
|
||||
"express-session": "^1.17.3",
|
||||
"http-signature": "^1.3.6",
|
||||
"https-proxy-agent": "^5.0.1",
|
||||
"ical.js": "^1.5.0",
|
||||
|
@ -65,6 +65,14 @@
|
|||
"mysql2": "^2.3.3",
|
||||
"nuxt-edge": "2.16.0-27720022.54e852f",
|
||||
"nuxt-i18n": "^6.28.1",
|
||||
"oauth2orize": "^1.11.1",
|
||||
"passport": "^0.6.0",
|
||||
"passport-anonymous": "^1.0.1",
|
||||
"passport-custom": "^1.1.1",
|
||||
"passport-http": "^0.3.0",
|
||||
"passport-http-bearer": "^1.0.1",
|
||||
"passport-oauth2-client-password": "^0.1.2",
|
||||
"passport-oauth2-client-public": "^0.0.1",
|
||||
"pg": "^8.8.0",
|
||||
"sequelize": "^6.23.0",
|
||||
"sequelize-slugify": "^1.6.2",
|
||||
|
@ -94,7 +102,6 @@
|
|||
},
|
||||
"resolutions": {
|
||||
"nth-check": "^2.0.1",
|
||||
"express-oauth-server/**/lodash": "^4.17.21",
|
||||
"glob-parent": "^5.1.2",
|
||||
"moment": "^2.29.2"
|
||||
},
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
<template lang='pug'>
|
||||
.d-flex.justify-space-around
|
||||
v-form.d-flex.justify-space-around(method='post' action='/oauth/authorize')
|
||||
v-card.mt-5(max-width='600px')
|
||||
v-card-title {{settings.title}} - {{$t('common.authorize')}}
|
||||
v-card-text
|
||||
h2 {{$auth.user.email}}
|
||||
input(name='transaction_id' :value='transactionID' type='hidden')
|
||||
u {{$auth.user.email}}
|
||||
|
||||
div
|
||||
p(v-html="$t('oauth.authorization_request', { app: client.name, instance_name: settings.title })")
|
||||
p(v-html="$t('oauth.authorization_request', { app: client, instance_name: settings.title })")
|
||||
ul.mb-2
|
||||
li(v-for="s in scope.split(' ')") {{$t(`oauth.scopes.${scope}`)}}
|
||||
span(v-html="$t('oauth.redirected_to', {url: $route.query.redirect_uri})")
|
||||
li {{$t(`oauth.scopes.${scope}`)}}
|
||||
span(v-html="$t('oauth.redirected_to', {url: redirect_uri})")
|
||||
v-card-actions
|
||||
v-spacer
|
||||
v-btn(color='error' to='/') {{$t('common.cancel')}}
|
||||
v-btn(:href='authorizeURL' color='success') {{$t('common.authorize')}}
|
||||
v-btn(color='error' to='/' outlined) {{$t('common.cancel')}}
|
||||
v-btn(type='submit' color='success' outlined) {{$t('common.authorize')}}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -23,58 +26,12 @@ export default {
|
|||
layout: 'modal',
|
||||
middleware: ['auth'],
|
||||
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 !== 'event:write') {
|
||||
err = 'scope is missing or wrong'
|
||||
}
|
||||
if (!response_type || response_type !== 'code') {
|
||||
err = 'response_type is missing or wrong'
|
||||
}
|
||||
|
||||
// retrieve client validity
|
||||
try {
|
||||
const client = await $axios.$get(`/client/${client_id}`)
|
||||
if (!client) {
|
||||
err = 'client not found'
|
||||
}
|
||||
if (err) {
|
||||
return error({ statusCode: 404, message: err })
|
||||
}
|
||||
return { client, redirect_uri, scope, response_type }
|
||||
} catch (e) {
|
||||
error({ statusCode: 400, message: 'Something goes wrong with OAuth authorization' })
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
client: { name: 'Test' }
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['settings']),
|
||||
authorizeURL () {
|
||||
const { scope, response_type, client_id, redirect_uri, state } = this.$route.query
|
||||
const query = `client_id=${client_id}&response_type=${response_type}&scope=${scope}&redirect_uri=${redirect_uri}&state=${state}`
|
||||
return `oauth/authorize?${query}`
|
||||
}
|
||||
const { transactionID, client, scope, redirect_uri } = query
|
||||
return { transactionID, client, redirect_uri, scope }
|
||||
},
|
||||
computed: mapState(['settings']),
|
||||
head () {
|
||||
return { title: `${this.settings.title} - Authorize` }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
h4 img {
|
||||
max-height: 40px;
|
||||
border-radius: 20px;
|
||||
background-color: #333;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
</style>
|
||||
</script>
|
|
@ -217,7 +217,9 @@ export default {
|
|||
formData.append('description', this.event.description)
|
||||
formData.append('multidate', !!this.date.multidate)
|
||||
formData.append('start_datetime', dayjs(this.date.from).unix())
|
||||
formData.append('end_datetime', this.date.due ? dayjs(this.date.due).unix() : dayjs(this.date.from).add(2, 'hour').unix())
|
||||
if (this.date.due) {
|
||||
formData.append('end_datetime', dayjs(this.date.due).unix())
|
||||
}
|
||||
|
||||
if (this.edit) {
|
||||
formData.append('id', this.event.id)
|
||||
|
|
|
@ -56,7 +56,6 @@ export default {
|
|||
data ({ $store }) {
|
||||
return {
|
||||
mdiMagnify, mdiCloseCircle,
|
||||
first: true,
|
||||
isCurrentMonth: true,
|
||||
now: dayjs().unix(),
|
||||
date: dayjs.tz().format('YYYY-MM-DD'),
|
||||
|
@ -93,7 +92,7 @@ export default {
|
|||
const max = dayjs.tz(this.selectedDay).endOf('day').unix()
|
||||
return this.events.filter(e => (e.start_datetime <= max && (e.end_datetime || e.start_datetime) >= min) && (this.show_recurrent || !e.parentId))
|
||||
} else if (this.isCurrentMonth) {
|
||||
return this.events.filter(e => ((e.end_datetime ? e.end_datetime > now : e.start_datetime + 2 * 60 * 60 > now) && (this.show_recurrent || !e.parentId)))
|
||||
return this.events.filter(e => ((e.end_datetime ? e.end_datetime > now : e.start_datetime + 3 * 60 * 60 > now) && (this.show_recurrent || !e.parentId)))
|
||||
} else {
|
||||
return this.events.filter(e => this.show_recurrent || !e.parentId)
|
||||
}
|
||||
|
@ -115,11 +114,6 @@ export default {
|
|||
})
|
||||
},
|
||||
monthChange ({ year, month }) {
|
||||
// avoid first time monthChange event (onload)
|
||||
if (this.first) {
|
||||
this.first = false
|
||||
return
|
||||
}
|
||||
|
||||
this.$nuxt.$loading.start()
|
||||
|
||||
|
|
|
@ -77,13 +77,12 @@ export default ({ app, store }) => {
|
|||
|
||||
Vue.filter('when', (event) => {
|
||||
const start = dayjs.unix(event.start_datetime).tz().locale(app.i18n.locale || store.state.settings.instance_locale)
|
||||
const end = dayjs.unix(event.end_datetime).tz().locale(app.i18n.locale || store.state.settings.instance_locale)
|
||||
// multidate
|
||||
if (event.multidate) {
|
||||
return `${start.format('dddd D MMMM HH:mm')} - ${end.format('dddd D MMMM HH:mm')}`
|
||||
}
|
||||
const end = event.end_datetime && dayjs.unix(event.end_datetime).tz().locale(app.i18n.locale || store.state.settings.instance_locale)
|
||||
|
||||
// normal event
|
||||
return `${start.format('dddd D MMMM HH:mm')}-${end.format('HH:mm')}`
|
||||
let time = start.format('dddd D MMMM HH:mm')
|
||||
if (end) {
|
||||
time += event.multidate ? `-${end.format('dddd D MMMM HH:mm')}` : `-${end.format('HH:mm')}`
|
||||
}
|
||||
return time
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,28 +1,138 @@
|
|||
const log = require('../log')
|
||||
const oauth = require('./oauth')
|
||||
const get = require('lodash/get')
|
||||
const passport = require('passport')
|
||||
|
||||
// const oauth = require('./oauth')
|
||||
// const User = require('./models/user')
|
||||
// const OAuthClient = require('./models/oauth_client')
|
||||
// const OAuthCode = require('./models/oauth_code')
|
||||
// const OAuthToken = require('./models/oauth_token')
|
||||
|
||||
|
||||
// const CustomStrategy = require('passport-custom').Strategy
|
||||
// const LocalStrategy = require('passport-local').Strategy
|
||||
// const BasicStrategy = require('passport-http').BasicStrategy
|
||||
// const ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy
|
||||
// const BearerStrategy = require('passport-http-bearer').Strategy
|
||||
|
||||
// console.error('dentro passport setup!')
|
||||
// passport.use('authenticate', new CustomStrategy(async (req, done) => {
|
||||
// console.error('dentro authenticate strategy')
|
||||
|
||||
// // check if a cookie is passed
|
||||
// const token = get(req.cookies, 'auth._token.local', null)
|
||||
// const authorization = get(req.headers, 'authorization', null)
|
||||
// if (!authorization && token) {
|
||||
// req.headers.authorization = token
|
||||
// }
|
||||
|
||||
// if (!authorization && !token) {
|
||||
// return done(null, false)
|
||||
// }
|
||||
|
||||
// console.error(authorization, token)
|
||||
// return done(null, false)
|
||||
|
||||
// }))
|
||||
|
||||
/**
|
||||
* LocalStrategy
|
||||
*
|
||||
* This strategy is used to authenticate users based on a username and password.
|
||||
* Anytime a request is made to authorize an application, we must ensure that
|
||||
* a user is logged in before asking them to approve the request.
|
||||
*/
|
||||
// passport.use(new LocalStrategy(
|
||||
// async (username, password, done) => {
|
||||
// console.error(`sono qui dentro local strategy cerco ${username} ${password}}`)
|
||||
// const user = await User.findOne({ where: { email: username, is_active: true } })
|
||||
// console.error(user)
|
||||
// if (!user) {
|
||||
// return done(null, false)
|
||||
// }
|
||||
// // check if password matches
|
||||
// if (await user.comparePassword(password)) {
|
||||
// console.error('compare password ok!')
|
||||
// return done(null, user)
|
||||
// }
|
||||
// return done(null, false)
|
||||
// }
|
||||
// // ))
|
||||
|
||||
// passport.serializeUser((user, done) => done(null, user.id))
|
||||
|
||||
// passport.deserializeUser(async (id, done) => {
|
||||
// const user = await User.findByPk(id)
|
||||
// done(null, user)
|
||||
// })
|
||||
|
||||
/**
|
||||
* BasicStrategy & ClientPasswordStrategy
|
||||
*
|
||||
* These strategies are used to authenticate registered OAuth clients. They are
|
||||
* employed to protect the `token` endpoint, which consumers use to obtain
|
||||
* access tokens. The OAuth 2.0 specification suggests that clients use the
|
||||
* HTTP Basic scheme to authenticate. Use of the client password strategy
|
||||
* allows clients to send the same credentials in the request body (as opposed
|
||||
* to the `Authorization` header). While this approach is not recommended by
|
||||
* the specification, in practice it is quite common.
|
||||
*/
|
||||
// async function verifyClient(client_id, client_secret, done) {
|
||||
// console.error('Dentro verify client ', client_id, client_secret)
|
||||
// const client = await OAuthClient.findByPk(client_id, { raw: true })
|
||||
// console.error(client)
|
||||
// if (client_secret && client_secret !== client.client_secret) {
|
||||
// return done(null, false)
|
||||
// }
|
||||
|
||||
// if (client) { client.grants = ['authorization_code', 'password'] } //sure ?
|
||||
|
||||
// return done(null, client)
|
||||
// }
|
||||
|
||||
// passport.use(new BasicStrategy(verifyClient))
|
||||
// passport.use(new ClientPasswordStrategy(verifyClient))
|
||||
|
||||
/**
|
||||
* BearerStrategy
|
||||
*
|
||||
* This strategy is used to authenticate either users or clients based on an access token
|
||||
* (aka a bearer token). If a user, they must have previously authorized a client
|
||||
* application, which is issued an access token to make requests on behalf of
|
||||
* the authorizing user.
|
||||
*/
|
||||
// passport.use(new BearerStrategy(
|
||||
// async (accessToken, done) => {
|
||||
// console.error('dentro bearer strategy')
|
||||
// const token = await OAuthToken.findByPk(accessToken,
|
||||
// { include: [{ model: User, attributes: { exclude: ['password'] } }, { model: OAuthClient, as: 'client' }] })
|
||||
|
||||
// if (!token) return done(null, false)
|
||||
// if (token.userId) {
|
||||
// if (!token.user) {
|
||||
// return done(null, false)
|
||||
// }
|
||||
// // To keep this example simple, restricted scopes are not implemented,
|
||||
// // and this is just for illustrative purposes.
|
||||
// done(null, user, { scope: '*' })
|
||||
// } else {
|
||||
// // The request came from a client only since userId is null,
|
||||
// // therefore the client is passed back instead of a user.
|
||||
// if (!token.client) {
|
||||
// return done(null, false)
|
||||
// }
|
||||
// // To keep this example simple, restricted scopes are not implemented,
|
||||
// // and this is just for illustrative purposes.
|
||||
// done(null, client, { scope: '*' })
|
||||
// }
|
||||
// }
|
||||
// ))
|
||||
|
||||
const Auth = {
|
||||
|
||||
fillUser (req, res, next) {
|
||||
const token = get(req.cookies, 'auth._token.local', null)
|
||||
const authorization = get(req.headers, 'authorization', null)
|
||||
if (!authorization && token) {
|
||||
req.headers.authorization = token
|
||||
}
|
||||
|
||||
if (!authorization && !token) {
|
||||
return next()
|
||||
}
|
||||
|
||||
oauth.oauthServer.authenticate()(req, res, () => {
|
||||
res.locals.user = get(res, 'locals.oauth.token.user', null)
|
||||
next()
|
||||
})
|
||||
},
|
||||
|
||||
isAuth (_req, res, next) {
|
||||
if (res.locals.user) {
|
||||
isAuth (req, res, next) {
|
||||
// TODO: check anon user
|
||||
if (req.user) {
|
||||
next()
|
||||
} else {
|
||||
res.sendStatus(403)
|
||||
|
@ -30,7 +140,7 @@ const Auth = {
|
|||
},
|
||||
|
||||
isAdmin (req, res, next) {
|
||||
if (res.locals.user && res.locals.user.is_admin) {
|
||||
if (req.user && req.user.is_admin) {
|
||||
next()
|
||||
} else {
|
||||
res.sendStatus(403)
|
||||
|
|
|
@ -196,7 +196,7 @@ const eventController = {
|
|||
|
||||
async get(req, res) {
|
||||
const format = req.params.format || 'json'
|
||||
const is_admin = res.locals.user && res.locals.user.is_admin
|
||||
const is_admin = req.user && req.user.is_admin
|
||||
const slug = req.params.event_slug
|
||||
|
||||
// retrocompatibility, old events URL does not use slug, use id as fallback
|
||||
|
@ -301,7 +301,7 @@ const eventController = {
|
|||
log.warn(`Trying to confirm a unknown event, id: ${id}`)
|
||||
return res.sendStatus(404)
|
||||
}
|
||||
if (!res.locals.user.is_admin && res.locals.user.id !== event.userId) {
|
||||
if (!req.user.is_admin && req.user.id !== event.userId) {
|
||||
log.warn(`Someone not allowed is trying to confirm -> "${event.title} `)
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
|
@ -327,7 +327,7 @@ const eventController = {
|
|||
const id = Number(req.params.event_id)
|
||||
const event = await Event.findByPk(id)
|
||||
if (!event) { return req.sendStatus(404) }
|
||||
if (!res.locals.user.is_admin && res.locals.user.id !== event.userId) {
|
||||
if (!req.user.is_admin && req.user.id !== event.userId) {
|
||||
log.warn(`Someone not allowed is trying to unconfirm -> "${event.title} `)
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
|
@ -386,8 +386,8 @@ const eventController = {
|
|||
res.sendStatus(200)
|
||||
},
|
||||
|
||||
async isAnonEventAllowed(_req, res, next) {
|
||||
if (!res.locals.settings.allow_anon_event && !res.locals.user) {
|
||||
async isAnonEventAllowed(req, res, next) {
|
||||
if (!res.locals.settings.allow_anon_event && !req.user) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
next()
|
||||
|
@ -432,7 +432,7 @@ const eventController = {
|
|||
end_datetime: body.end_datetime,
|
||||
recurrent,
|
||||
// publish this event only if authenticated
|
||||
is_visible: !!res.locals.user
|
||||
is_visible: !!req.user
|
||||
}
|
||||
|
||||
if (req.file || body.image_url) {
|
||||
|
@ -466,9 +466,9 @@ const eventController = {
|
|||
}
|
||||
|
||||
// associate user to event and reverse
|
||||
if (res.locals.user) {
|
||||
await res.locals.user.addEvent(event)
|
||||
await event.setUser(res.locals.user)
|
||||
if (req.user) {
|
||||
await req.user.addEvent(event)
|
||||
await event.setUser(req.user)
|
||||
}
|
||||
|
||||
event = event.get()
|
||||
|
@ -502,7 +502,7 @@ const eventController = {
|
|||
const body = req.body
|
||||
const event = await Event.findByPk(body.id)
|
||||
if (!event) { return res.sendStatus(404) }
|
||||
if (!res.locals.user.is_admin && event.userId !== res.locals.user.id) {
|
||||
if (!req.user.is_admin && event.userId !== req.user.id) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
|
||||
|
@ -596,7 +596,7 @@ const eventController = {
|
|||
async remove(req, res) {
|
||||
const event = await Event.findByPk(req.params.id)
|
||||
// check if event is mine (or user is admin)
|
||||
if (event && (res.locals.user.is_admin || res.locals.user.id === event.userId)) {
|
||||
if (event && (req.user.is_admin || req.user.id === event.userId)) {
|
||||
if (event.media && event.media.length && !event.recurrent) {
|
||||
try {
|
||||
const old_path = path.join(config.upload_path, event.media[0].url)
|
||||
|
|
|
@ -1,26 +1,357 @@
|
|||
const crypto = require('crypto')
|
||||
const { promisify } = require('util')
|
||||
const randomBytes = promisify(crypto.randomBytes)
|
||||
const bodyParser = require('body-parser')
|
||||
const cookieParser = require('cookie-parser')
|
||||
const session = require('express-session')
|
||||
|
||||
const OAuthClient = require('../models/oauth_client')
|
||||
const OAuthToken = require('../models/oauth_token')
|
||||
const OAuthCode = require('../models/oauth_code')
|
||||
|
||||
const helpers = require('../../helpers.js')
|
||||
const User = require('../models/user')
|
||||
const passport = require('passport')
|
||||
|
||||
const get = require('lodash/get')
|
||||
|
||||
const BasicStrategy = require('passport-http').BasicStrategy
|
||||
const ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy
|
||||
const ClientPublicStrategy = require('passport-oauth2-client-public').Strategy
|
||||
const BearerStrategy = require('passport-http-bearer').Strategy
|
||||
const AnonymousStrategy = require('passport-anonymous').Strategy
|
||||
|
||||
const oauth2orize = require('oauth2orize')
|
||||
const log = require('../../log')
|
||||
const dayjs = require('dayjs')
|
||||
|
||||
async function randomString (len = 16) {
|
||||
const bytes = await randomBytes(len * 8)
|
||||
return crypto
|
||||
.createHash('sha1')
|
||||
.update(bytes)
|
||||
.digest('hex')
|
||||
passport.serializeUser((user, done) => done(null, user.id))
|
||||
|
||||
passport.deserializeUser(async (id, done) => {
|
||||
const user = await User.findByPk(id)
|
||||
done(null, user)
|
||||
})
|
||||
|
||||
/**
|
||||
* BasicStrategy & ClientPasswordStrategy
|
||||
*
|
||||
* These strategies are used to authenticate registered OAuth clients. They are
|
||||
* employed to protect the `token` endpoint, which consumers use to obtain
|
||||
* access tokens. The OAuth 2.0 specification suggests that clients use the
|
||||
* HTTP Basic scheme to authenticate. Use of the client password strategy
|
||||
* allows clients to send the same credentials in the request body (as opposed
|
||||
* to the `Authorization` header). While this approach is not recommended by
|
||||
* the specification, in practice it is quite common.
|
||||
*/
|
||||
async function verifyClient(client_id, client_secret, done) {
|
||||
const client = await OAuthClient.findByPk(client_id, { raw: true })
|
||||
if (!client) {
|
||||
return done(null, false)
|
||||
}
|
||||
if (client.client_secret && client_secret !== client.client_secret) {
|
||||
return done(null, false)
|
||||
}
|
||||
|
||||
if (client) { client.grants = ['authorization_code', 'password'] } //sure ?
|
||||
|
||||
return done(null, client)
|
||||
}
|
||||
|
||||
const oauthController = {
|
||||
async function verifyPublicClient (client_id, done) {
|
||||
if (client_id !== 'self') {
|
||||
return done(null, false)
|
||||
}
|
||||
try {
|
||||
|
||||
// create client => http:///gancio.org/oauth#create-client
|
||||
const client = await OAuthClient.findByPk(client_id, { raw: true })
|
||||
done(null, client)
|
||||
} catch (e) {
|
||||
done(null, { message: e.message })
|
||||
}
|
||||
}
|
||||
|
||||
passport.use(new AnonymousStrategy())
|
||||
passport.use(new BasicStrategy(verifyClient))
|
||||
passport.use(new ClientPasswordStrategy(verifyClient))
|
||||
passport.use(new ClientPublicStrategy(verifyPublicClient))
|
||||
|
||||
/**
|
||||
* BearerStrategy
|
||||
*
|
||||
* This strategy is used to authenticate either users or clients based on an access token
|
||||
* (aka a bearer token). If a user, they must have previously authorized a client
|
||||
* application, which is issued an access token to make requests on behalf of
|
||||
* the authorizing user.
|
||||
*/
|
||||
passport.use(new BearerStrategy({ passReqToCallback: true }, verifyToken))
|
||||
|
||||
async function verifyToken (req, accessToken, done) {
|
||||
const token = await OAuthToken.findByPk(accessToken,
|
||||
{ include: [{ model: User, attributes: { exclude: ['password'] } }, { model: OAuthClient, as: 'client' }] })
|
||||
|
||||
if (!token) return done(null, false)
|
||||
if (token.userId) {
|
||||
if (!token.user) {
|
||||
return done(null, false)
|
||||
}
|
||||
// To keep this example simple, restricted scopes are not implemented,
|
||||
// and this is just for illustrative purposes.
|
||||
done(null, token.user, { scope: '*' })
|
||||
} else {
|
||||
|
||||
// The request came from a client only since userId is null,
|
||||
// therefore the client is passed back instead of a user.
|
||||
if (!token.client) {
|
||||
return done(null, false)
|
||||
}
|
||||
// To keep this example simple, restricted scopes are not implemented,
|
||||
// and this is just for illustrative purposes.
|
||||
done(null, client, { scope: '*' })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const oauthServer = oauth2orize.createServer()
|
||||
|
||||
|
||||
// Register serialization and deserialization functions.
|
||||
//
|
||||
// When a client redirects a user to user authorization endpoint, an
|
||||
// authorization transaction is initiated. To complete the transaction, the
|
||||
// user must authenticate and approve the authorization request. Because this
|
||||
// may involve multiple HTTP request/response exchanges, the transaction is
|
||||
// stored in the session.
|
||||
//
|
||||
// An application must supply serialization functions, which determine how the
|
||||
// client object is serialized into the session. Typically this will be a
|
||||
// simple matter of serializing the client's ID, and deserializing by finding
|
||||
// the client by ID from the database.
|
||||
oauthServer.serializeClient((client, done) => {
|
||||
done(null, client.id)
|
||||
})
|
||||
|
||||
oauthServer.deserializeClient(async (id, done) => {
|
||||
const client = await OAuthClient.findByPk(id)
|
||||
done(null, client)
|
||||
})
|
||||
|
||||
// Register supported grant types.
|
||||
//
|
||||
// OAuth 2.0 specifies a framework that allows users to grant client
|
||||
// applications limited access to their protected resources. It does this
|
||||
// through a process of the user granting access, and the client exchanging
|
||||
// the grant for an access token.
|
||||
|
||||
// Grant authorization codes. The callback takes the `client` requesting
|
||||
// authorization, the `redirectUri` (which is used as a verifier in the
|
||||
// subsequent exchange), the authenticated `user` granting access, and
|
||||
// their response, which contains approved scope, duration, etc. as parsed by
|
||||
// the application. The application issues a code, which is bound to these
|
||||
// values, and will be exchanged for an access token.
|
||||
|
||||
oauthServer.grant(oauth2orize.grant.code(async (client, redirect_uri, user, ares, done) => {
|
||||
const authorizationCode = helpers.randomString(16);
|
||||
await OAuthCode.create({
|
||||
redirect_uri,
|
||||
authorizationCode,
|
||||
clientId: client.id,
|
||||
userId: user.id,
|
||||
})
|
||||
return done(null, authorizationCode)
|
||||
}))
|
||||
|
||||
|
||||
// Grant implicit authorization. The callback takes the `client` requesting
|
||||
// authorization, the authenticated `user` granting access, and
|
||||
// their response, which contains approved scope, duration, etc. as parsed by
|
||||
// the application. The application issues a token, which is bound to these
|
||||
// values.
|
||||
|
||||
oauthServer.grant(oauth2orize.grant.token((client, user, ares, done) => {
|
||||
return oauthController.issueTokens(user.id, client.clientId, done)
|
||||
}))
|
||||
|
||||
|
||||
// Exchange authorization codes for access tokens. The callback accepts the
|
||||
// `client`, which is exchanging `code` and any `redirectUri` from the
|
||||
// authorization request for verification. If these values are validated, the
|
||||
// application issues an access token on behalf of the user who authorized the
|
||||
// code. The issued access token response can include a refresh token and
|
||||
// custom parameters by adding these to the `done()` call
|
||||
|
||||
oauthServer.exchange(oauth2orize.exchange.code(async (client, code, redirect_uri, done) => {
|
||||
const oauthCode = await OAuthCode.findByPk(code)
|
||||
if (!oauthCode || client.id !== oauthCode.clientId || client.redirectUris !== oauthCode.redirect_uri) {
|
||||
return done(null, false)
|
||||
}
|
||||
return oauthController.issueTokens(oauthCode.userId, oauthCode.clientId, done)
|
||||
}))
|
||||
|
||||
|
||||
|
||||
// Exchange user id and password for access tokens. The callback accepts the
|
||||
// `client`, which is exchanging the user's name and password from the
|
||||
// authorization request for verification. If these values are validated, the
|
||||
// application issues an access token on behalf of the user who authorized the code.
|
||||
oauthServer.exchange(oauth2orize.exchange.password(async (client, username, password, scope, done) => {
|
||||
// Validate the client
|
||||
const oauthClient = await OAuthClient.findByPk(client.id)
|
||||
if (!oauthClient) { // || oauthClient.client_secret !== client.clientSecret) {
|
||||
return done(null, false)
|
||||
}
|
||||
const user = await User.findOne({ where: { email: username, is_active: true } })
|
||||
if (!user) {
|
||||
return done(null, false)
|
||||
}
|
||||
// check if password matches
|
||||
if (await user.comparePassword(password)) {
|
||||
return oauthController.issueTokens(user.id, oauthClient.id, done)
|
||||
}
|
||||
return done(null, false)
|
||||
}))
|
||||
|
||||
|
||||
// Exchange the client id and password/secret for an access token. The callback accepts the
|
||||
// `client`, which is exchanging the client's id and password/secret from the
|
||||
// authorization request for verification. If these values are validated, the
|
||||
// application issues an access token on behalf of the client who authorized the code.
|
||||
oauthServer.exchange(oauth2orize.exchange.clientCredentials(async (client, scope, done) => {
|
||||
// Validate the client
|
||||
const oauthClient = await OAuthClient.findByPk(client.clientId)
|
||||
if (!oauthClient || oauthClient.client_secret !== client.clientSecret) {
|
||||
return done(null, false)
|
||||
}
|
||||
|
||||
return oauthController.issueTokens(null, oauthClient.id, done)
|
||||
}))
|
||||
|
||||
// issue new tokens and remove the old ones
|
||||
oauthServer.exchange(oauth2orize.exchange.refreshToken(async (client, refreshToken, scope, done) => {
|
||||
// db.refreshTokens.find(refreshToken, (error, token) => {
|
||||
// if (error) return done(error)
|
||||
// issueTokens(token.id, client.id, (err, accessToken, refreshToken) => {
|
||||
// if (err) {
|
||||
// done(err, null, null)
|
||||
// }
|
||||
// db.accessTokens.removeByUserIdAndClientId(token.userId, token.clientId, (err) => {
|
||||
// if (err) {
|
||||
// done(err, null, null)
|
||||
// }
|
||||
// db.refreshTokens.removeByUserIdAndClientId(token.userId, token.clientId, (err) => {
|
||||
// if (err) {
|
||||
// done(err, null, null)
|
||||
// }
|
||||
// done(null, accessToken, refreshToken)
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
}))
|
||||
|
||||
|
||||
const oauthController = {
|
||||
|
||||
// this is a middleware to authenticate a request
|
||||
authenticate: [
|
||||
passport.initialize(), // initialize passport
|
||||
cookieParser(), // parse cookies
|
||||
session({ secret: 'secret', resave: true, saveUninitialized: true }),
|
||||
passport.session(),
|
||||
(req, res, next) => { // retrocompatibility
|
||||
const token = get(req.cookies, 'auth._token.local', null)
|
||||
const authorization = get(req.headers, 'authorization', null)
|
||||
if (!authorization && token) {
|
||||
req.headers.authorization = token
|
||||
}
|
||||
next()
|
||||
},
|
||||
passport.authenticate(['bearer', 'oauth2-client-password', 'anonymous'], { session: false })
|
||||
],
|
||||
|
||||
login: [
|
||||
bodyParser.urlencoded({ extended: true }), // login is done via application/x-www-form-urlencoded form
|
||||
passport.authenticate(['oauth2-client-public'], { session: false }),
|
||||
oauthServer.token(),
|
||||
oauthServer.errorHandler()
|
||||
],
|
||||
|
||||
token: [
|
||||
bodyParser.urlencoded({ extended: true }), // login is done via application/x-www-form-urlencoded form
|
||||
passport.authenticate(['bearer', 'oauth2-client-password'], { session: false }),
|
||||
oauthServer.token(),
|
||||
oauthServer.errorHandler()
|
||||
],
|
||||
|
||||
authorization: [
|
||||
oauthServer.authorization(async (clientId, redirectUri, done) => {
|
||||
const oauthClient = await OAuthClient.findByPk(clientId)
|
||||
if (!oauthClient) {
|
||||
return done(null, false)
|
||||
}
|
||||
|
||||
// WARNING: For security purposes, it is highly advisable to check that
|
||||
// redirectUri provided by the client matches one registered with
|
||||
// the server. For simplicity, this example does not. You have
|
||||
// been warned.
|
||||
return done(null, oauthClient, redirectUri);
|
||||
}, async (client, user, done) => {
|
||||
// Check if grant request qualifies for immediate approval
|
||||
|
||||
// Auto-approve
|
||||
if (client.isTrusted) return done(null, true);
|
||||
if (!user) {
|
||||
return done(null, false)
|
||||
}
|
||||
const token = await OAuthToken.findOne({ where: { clientId: client.id, userId: user.id }})
|
||||
// Auto-approve
|
||||
if (token) {
|
||||
return done(null, true)
|
||||
}
|
||||
// Otherwise ask user
|
||||
return done(null, false)
|
||||
|
||||
}),
|
||||
(req, res, next) => {
|
||||
//clean old transactionID
|
||||
if(req.session.authorize){
|
||||
for(const key in req.session.authorize){
|
||||
if(key !== req.oauth2.transactionID){
|
||||
delete req.session.authorize[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
const query = new URLSearchParams({
|
||||
transactionID: req.oauth2.transactionID,
|
||||
client: req.oauth2.client.name,
|
||||
scope: req.oauth2.client.scopes,
|
||||
redirect_uri: req.oauth2.client.redirectUris
|
||||
})
|
||||
return res.redirect(`/authorize?${query.toString()}`)
|
||||
}
|
||||
],
|
||||
|
||||
decision: [
|
||||
bodyParser.urlencoded({ extended: true }),
|
||||
oauthServer.decision()
|
||||
],
|
||||
|
||||
async issueTokens(userId, clientId, done) {
|
||||
const user = await User.findByPk(userId)
|
||||
if (!user) {
|
||||
return done(null, false)
|
||||
}
|
||||
|
||||
const refreshToken = helpers.randomString(32)
|
||||
const accessToken = helpers.randomString(32)
|
||||
|
||||
const token = {
|
||||
refreshToken,
|
||||
accessToken,
|
||||
userId,
|
||||
clientId
|
||||
}
|
||||
|
||||
await OAuthToken.create(token)
|
||||
return done(null, accessToken, refreshToken, { username: user.email })
|
||||
},
|
||||
|
||||
// create client => http:///gancio.org/dev/oauth#create-client
|
||||
async createClient (req, res) {
|
||||
// only write scope is supported
|
||||
if (req.body.scopes && req.body.scopes !== 'event:write') {
|
||||
|
@ -28,12 +359,12 @@ const oauthController = {
|
|||
}
|
||||
|
||||
const client = {
|
||||
id: await randomString(256),
|
||||
id: helpers.randomString(32),
|
||||
name: req.body.client_name,
|
||||
redirectUris: req.body.redirect_uris,
|
||||
scopes: req.body.scopes || 'event:write',
|
||||
website: req.body.website,
|
||||
client_secret: await randomString(256)
|
||||
client_secret: helpers.randomString(32)
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -63,99 +394,11 @@ const oauthController = {
|
|||
|
||||
async getClients (req, res) {
|
||||
const tokens = await OAuthToken.findAll({
|
||||
include: [{ model: User, where: { id: res.locals.user.id } }, { model: OAuthClient, as: 'client' }],
|
||||
include: [{ model: User, where: { id: req.user.id } }, { model: OAuthClient, as: 'client' }],
|
||||
raw: true,
|
||||
nest: true
|
||||
})
|
||||
res.json(tokens)
|
||||
},
|
||||
|
||||
model: {
|
||||
|
||||
/**
|
||||
* Invoked to retrieve an existing access token previously saved through #saveToken().
|
||||
* https://oauth2-server.readthedocs.io/en/latest/model/spec.html#getaccesstoken-accesstoken-callback
|
||||
* */
|
||||
async getAccessToken (accessToken) {
|
||||
const oauth_token = await OAuthToken.findByPk(accessToken,
|
||||
{ include: [{ model: User, attributes: { exclude: ['password'] } }, { model: OAuthClient, as: 'client' }] })
|
||||
return oauth_token
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked to retrieve a client using a client id or a client id/client secret combination, depend on the grant type.
|
||||
*/
|
||||
async getClient (client_id, client_secret) {
|
||||
const client = await OAuthClient.findByPk(client_id, { raw: true })
|
||||
if (!client || (client_secret && client_secret !== client.client_secret)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (client) { client.grants = ['authorization_code', 'password'] }
|
||||
|
||||
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, { model: OAuthClient, as: 'client' }] })
|
||||
return oauth_code
|
||||
},
|
||||
|
||||
async saveToken (token, client, user) {
|
||||
token.userId = user.id
|
||||
token.clientId = 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.authorizationCode)
|
||||
return oauth_code.destroy()
|
||||
},
|
||||
|
||||
async getUser (username, password) {
|
||||
const user = await User.findOne({ where: { email: username } })
|
||||
if (!user || !user.is_active) {
|
||||
return false
|
||||
}
|
||||
// check if password matches
|
||||
if (await user.comparePassword(password)) {
|
||||
return user
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
async saveAuthorizationCode (code, client, user) {
|
||||
code.userId = user.id
|
||||
code.clientId = client.id
|
||||
code.expiresAt = dayjs(code.expiresAt).toDate()
|
||||
return OAuthCode.create(code)
|
||||
},
|
||||
|
||||
// TODO
|
||||
verifyScope (token, scope) {
|
||||
// const userScope = [
|
||||
// 'user:remove',
|
||||
// 'user:update',
|
||||
// 'event:write',
|
||||
// 'event:remove'
|
||||
// ]
|
||||
log.debug(`VERIFY SCOPE ${scope} ${token.user.email}`)
|
||||
if (token.user.is_admin && token.user.is_active) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,13 +44,13 @@ const userController = {
|
|||
},
|
||||
|
||||
async current (req, res) {
|
||||
if (!res.locals.user) { return res.status(400).send('Not logged') }
|
||||
const user = await User.scope('withoutPassword').findByPk(res.locals.user.id)
|
||||
if (!req.user) { return res.status(400).send('Not logged') }
|
||||
const user = await User.scope('withoutPassword').findByPk(req.user.id)
|
||||
res.json(user)
|
||||
},
|
||||
|
||||
async getAll (req, res) {
|
||||
const users = await User.scope(res.locals.user.is_admin ? 'withRecover' : 'withoutPassword').findAll({
|
||||
const users = await User.scope(req.user.is_admin ? 'withRecover' : 'withoutPassword').findAll({
|
||||
order: [['is_admin', 'DESC'], ['createdAt', 'DESC']]
|
||||
})
|
||||
res.json(users)
|
||||
|
@ -62,7 +62,7 @@ const userController = {
|
|||
|
||||
if (!user) { return res.status(404).json({ success: false, message: 'User not found!' }) }
|
||||
|
||||
if (req.body.id !== res.locals.user.id && !res.locals.user.is_admin) {
|
||||
if (req.body.id !== req.user.id && !req.user.is_admin) {
|
||||
return res.status(400).json({ succes: false, message: 'Not allowed' })
|
||||
}
|
||||
|
||||
|
@ -123,10 +123,10 @@ const userController = {
|
|||
async remove (req, res) {
|
||||
try {
|
||||
let user
|
||||
if (res.locals.user.is_admin && req.params.id) {
|
||||
if (req.user.is_admin && req.params.id) {
|
||||
user = await User.findByPk(req.params.id)
|
||||
} else {
|
||||
user = await User.findByPk(res.locals.user.id)
|
||||
user = await User.findByPk(req.user.id)
|
||||
}
|
||||
await user.destroy()
|
||||
log.warn(`User ${user.email} removed!`)
|
||||
|
|
|
@ -60,7 +60,7 @@ if (config.status !== 'READY') {
|
|||
```
|
||||
*/
|
||||
api.get('/ping', (_req, res) => res.sendStatus(200))
|
||||
api.get('/user', isAuth, (_req, res) => res.json(res.locals.user))
|
||||
api.get('/user', isAuth, (req, res) => res.json(req.user))
|
||||
|
||||
|
||||
api.post('/user/recover', userController.forgotPassword)
|
||||
|
|
|
@ -1,41 +1,51 @@
|
|||
const express = require('express')
|
||||
const OAuthServer = require('express-oauth-server')
|
||||
const oauth = express.Router()
|
||||
const oauthController = require('./controller/oauth')
|
||||
const log = require('../log')
|
||||
// const express = require('express')
|
||||
// // const OAuthServer = require('express-oauth-server')
|
||||
// const oauth2orize = require('oauth2orize')
|
||||
// // const oauth = express.Router()
|
||||
// // const oauthController = require('./controller/oauth')
|
||||
// // const OauthClient = require('./models/oauth_client')
|
||||
// // const log = require('../log')
|
||||
|
||||
const oauthServer = new OAuthServer({
|
||||
model: oauthController.model,
|
||||
allowEmptyState: true,
|
||||
useErrorHandler: true,
|
||||
continueMiddleware: false,
|
||||
debug: true,
|
||||
requireClientAuthentication: { password: false },
|
||||
authenticateHandler: {
|
||||
handle (_req, res) {
|
||||
if (!res.locals.user) {
|
||||
throw new Error('Not authenticated!')
|
||||
}
|
||||
return res.locals.user
|
||||
}
|
||||
}
|
||||
})
|
||||
// // const oauthServer = oauth2orize.createServer()
|
||||
|
||||
oauth.oauthServer = oauthServer
|
||||
oauth.use(express.json())
|
||||
oauth.use(express.urlencoded({ extended: false }))
|
||||
// /* model: oauthController.model, */
|
||||
// /* allowEmptyState: true, */
|
||||
// /* useErrorHandler: true, */
|
||||
// /* continueMiddleware: false, */
|
||||
// /* debug: true, */
|
||||
// /* requireClientAuthentication: { password: false }, */
|
||||
// /* authenticateHandler: { */
|
||||
// /* handle (_req, res) { */
|
||||
// /* if (!res.locals.user) { */
|
||||
// /* throw new Error('Not authenticated!') */
|
||||
// /* } */
|
||||
// /* return res.locals.user */
|
||||
// /* } */
|
||||
// /* } */
|
||||
// /* }) */
|
||||
|
||||
oauth.post('/token', oauthServer.token())
|
||||
oauth.post('/login', oauthServer.token())
|
||||
// // oauth.oauthServer = oauthServer
|
||||
// // oauth.use(express.json())
|
||||
// // oauth.use(express.urlencoded({ extended: false }))
|
||||
|
||||
oauth.get('/authorize', oauthServer.authorize())
|
||||
|
||||
oauth.use((req, res) => res.sendStatus(404))
|
||||
// oauthServer.serializeClient((client, done) => done(null, client.id))
|
||||
// oauthServer.deserializeClient(async (id, done) => {
|
||||
// const client = await OAuthServer.findByPk(id)
|
||||
// done(null, client)
|
||||
// })
|
||||
|
||||
oauth.use((err, req, res, next) => {
|
||||
const error_msg = err.toString()
|
||||
log.warn('[OAUTH USE] ' + error_msg)
|
||||
res.status(500).send(error_msg)
|
||||
})
|
||||
|
||||
module.exports = oauth
|
||||
// oauth.post('/token', oauthController.token)
|
||||
// oauth.post('/login', oauthController.token)
|
||||
// oauth.get('/authorize', oauthController.authorize)
|
||||
|
||||
// oauth.use((req, res) => res.sendStatus(404))
|
||||
|
||||
// oauth.use((err, req, res, next) => {
|
||||
// const error_msg = err.toString()
|
||||
// log.warn('[OAUTH USE] ' + error_msg)
|
||||
// res.status(500).send(error_msg)
|
||||
// })
|
||||
|
||||
// module.exports = oauth
|
||||
|
|
|
@ -48,7 +48,7 @@ domPurify.addHook('beforeSanitizeElements', node => {
|
|||
module.exports = {
|
||||
|
||||
randomString(length = 12) {
|
||||
const wishlist = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||||
const wishlist = '0123456789abcdefghijklmnopqrstuvwxyz'
|
||||
return Array.from(crypto.randomFillSync(new Uint32Array(length)))
|
||||
.map(x => wishlist[x % wishlist.length])
|
||||
.join('')
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
const express = require('express')
|
||||
const cookieParser = require('cookie-parser')
|
||||
const app = express()
|
||||
const initialize = require('./initialize.server')
|
||||
|
||||
const config = require('./config')
|
||||
const helpers = require('./helpers')
|
||||
app.use(helpers.initSettings)
|
||||
app.use(helpers.logRequest)
|
||||
app.use(helpers.serveStatic())
|
||||
app.use(cookieParser())
|
||||
|
||||
app.use([
|
||||
helpers.initSettings,
|
||||
helpers.logRequest,
|
||||
helpers.serveStatic()
|
||||
])
|
||||
|
||||
async function main () {
|
||||
|
||||
|
@ -18,7 +19,6 @@ async function main () {
|
|||
// const promBundle = require('express-prom-bundle')
|
||||
// const metricsMiddleware = promBundle({ includeMethod: true })
|
||||
|
||||
|
||||
const log = require('./log')
|
||||
const api = require('./api')
|
||||
|
||||
|
@ -28,14 +28,13 @@ async function main () {
|
|||
if (config.status === 'READY') {
|
||||
const cors = require('cors')
|
||||
const { spamFilter } = require('./federation/helpers')
|
||||
const oauth = require('./api/oauth')
|
||||
const auth = require('./api/auth')
|
||||
const federation = require('./federation')
|
||||
const webfinger = require('./federation/webfinger')
|
||||
const exportController = require('./api/controller/export')
|
||||
const tagController = require('./api/controller/tag')
|
||||
const placeController = require('./api/controller/place')
|
||||
const collectionController = require('./api/controller/collection')
|
||||
const authController = require('./api/controller/oauth')
|
||||
|
||||
// rss / ics feed
|
||||
app.use(helpers.feedRedirect)
|
||||
|
@ -43,7 +42,6 @@ async function main () {
|
|||
app.get('/feed/:format/place/:placeName', cors(), placeController.getEvents)
|
||||
app.get('/feed/:format/collection/:name', cors(), collectionController.getEvents)
|
||||
app.get('/feed/:format', cors(), exportController.export)
|
||||
|
||||
|
||||
app.use('/event/:slug', helpers.APRedirect)
|
||||
|
||||
|
@ -54,11 +52,11 @@ async function main () {
|
|||
// ignore unimplemented ping url from fediverse
|
||||
app.use(spamFilter)
|
||||
|
||||
// fill res.locals.user if request is authenticated
|
||||
app.use(auth.fillUser)
|
||||
|
||||
app.use('/oauth', oauth)
|
||||
// app.use(metricsMiddleware)
|
||||
app.use(authController.authenticate)
|
||||
app.post('/oauth/token', authController.token)
|
||||
app.post('/oauth/login', authController.login)
|
||||
app.get('/oauth/authorize', authController.authorization)
|
||||
app.post('/oauth/authorize', authController.decision)
|
||||
}
|
||||
|
||||
// api!
|
||||
|
@ -89,6 +87,8 @@ if (process.env.NODE_ENV !== 'test') {
|
|||
main()
|
||||
}
|
||||
|
||||
// app.listen(13120)
|
||||
|
||||
module.exports = {
|
||||
main,
|
||||
handler: app,
|
||||
|
|
|
@ -54,7 +54,8 @@ describe('Authentication / Authorization', () => {
|
|||
test('should not authenticate with wrong user/password', () => {
|
||||
return request(app).post('/oauth/login')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.expect(500)
|
||||
.send({ email: 'admin', password: 'wrong'})
|
||||
.expect(401)
|
||||
})
|
||||
|
||||
test('should register an admin as first user', async () => {
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
update()
|
||||
})
|
||||
$: update(
|
||||
maxlength && title && places && tags && theme && show_recurrent && sidebar
|
||||
maxlength && title && places && tags && theme && show_recurrent && sidebar && baseurl
|
||||
)
|
||||
</script>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
Plugin Name: WPGancio
|
||||
Plugin URI: https://gancio.org
|
||||
Description: Connects an user of a gancio instance to a Wordpress user so that published events are automatically pushed with Gancio API.
|
||||
Version: 1.0
|
||||
Version: 1.4
|
||||
Author: Gancio
|
||||
License: AGPL 3.0
|
||||
|
||||
|
@ -20,9 +20,11 @@ along with (WPGancio). If not, see (https://www.gnu.org/licenses/agpl-3.0.html).
|
|||
*/
|
||||
|
||||
defined( 'ABSPATH' ) or die( 'Nope, not accessing this' );
|
||||
require_once('settings.php');
|
||||
require_once('wc.php');
|
||||
require_once('oauth.php');
|
||||
define( 'WPGANCIO_DIR', plugin_dir_path( __FILE__ ) );
|
||||
require_once(WPGANCIO_DIR . 'settings.php');
|
||||
require_once(WPGANCIO_DIR . 'network_settings.php');
|
||||
require_once(WPGANCIO_DIR . 'wc.php');
|
||||
require_once(WPGANCIO_DIR . 'oauth.php');
|
||||
|
||||
|
||||
/**
|
||||
|
|
112
wp-plugin/network_settings.php
Normal file
112
wp-plugin/network_settings.php
Normal file
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
defined( 'ABSPATH' ) or die( 'Nope, not accessing this' );
|
||||
// https://codex.wordpress.org/Settings_API
|
||||
|
||||
if (!is_network_admin()) { return; }
|
||||
|
||||
// Fires before the administration menu loads in the admin, add our options page
|
||||
// add_action( 'admin_menu', 'wpgancio_options_page' );
|
||||
add_action('network_admin_menu', 'wpgancio_network_options_page');
|
||||
add_action('network_admin_edit_wpgancio', 'wpgancio_update');
|
||||
// add_action( 'update_option_wpgancio_instance_url', 'wpgancio_update_options', 15, 2);
|
||||
|
||||
function wpgancio_update () {
|
||||
$instance_url = get_site_option('wpgancio_instance_url');
|
||||
// // check_admin_referer( $this->settings_slug . '-page-options' );
|
||||
|
||||
// function wpgancio_update_options ($old_value, $instance_url) {
|
||||
$redirect_uri = network_admin_url('settings.php?page=wpgancio');
|
||||
$query = join('&', array(
|
||||
'response_type=code',
|
||||
'redirect_uri=' . esc_url($redirect_uri),
|
||||
'scope=event:write',
|
||||
'client_id=' . get_site_option('wpgancio_client_id'),
|
||||
));
|
||||
|
||||
wp_redirect("${instance_url}/oauth/authorize?${query}");
|
||||
exit;
|
||||
}
|
||||
|
||||
function wpgancio_network_options_page () {
|
||||
add_submenu_page('settings.php', 'Gancio', 'Gancio', 'manage_options', 'wpgancio', 'wpgancio_network_options_page_html');
|
||||
}
|
||||
|
||||
// function wpgancio_options_page() {
|
||||
// // add top level menu page
|
||||
// add_options_page(
|
||||
// 'Gancio',
|
||||
// 'Gancio',
|
||||
// 'manage_options',
|
||||
// 'wpgancio',
|
||||
// 'wpgancio_options_page_html'
|
||||
// );
|
||||
// }
|
||||
|
||||
// instance url field cb
|
||||
// field callbacks can accept an $args parameter, which is an array.
|
||||
// $args is defined at the add_settings_field() function.
|
||||
// wordpress has magic interaction with the following keys: label_for, class.
|
||||
// the "label_for" key value is used for the "for" attribute of the <label>.
|
||||
// the "class" key value is used for the "class" attribute of the <tr> containing the field.
|
||||
// you can add custom key value pairs to be used inside your callbacks.
|
||||
|
||||
/**
|
||||
* top level menu:
|
||||
* callback functions
|
||||
*/
|
||||
function wpgancio_network_options_page_html() {
|
||||
// check user capabilities
|
||||
if (! current_user_can('manage_network_options')) { return; }
|
||||
|
||||
// show error/update messages
|
||||
$code = sanitize_key(isset($_GET['code']) ? $_GET['code'] : '');
|
||||
if ( $code ) {
|
||||
update_site_option('wpgancio_code', $code);
|
||||
$instance_url = get_site_option( 'wpgancio_instance_url' );
|
||||
|
||||
$response = wp_remote_post($instance_url . "/oauth/token", array(
|
||||
'body' => array(
|
||||
'client_id' => get_site_option('wpgancio_client_id'),
|
||||
'client_secret' => get_site_option('wpgancio_client_secret'),
|
||||
'scope' => 'event:write',
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $code
|
||||
)));
|
||||
if (is_wp_error( $response ) ) {
|
||||
add_settings_error('wpgancio_messages', 'wpgancio_messages', $response->get_error_message());
|
||||
settings_errors( 'wpgancio_messages' );
|
||||
} elseif ( $response['response']['code'] != 200 ) {
|
||||
add_settings_error('wpgancio_messages', 'wpgancio_messages', $response['response']['code'] . ' ' . wp_remote_retrieve_body($response));
|
||||
settings_errors( 'wpgancio_messages' );
|
||||
} else {
|
||||
$data = json_decode( wp_remote_retrieve_body($response), true);
|
||||
update_site_option('wpgancio_token', sanitize_key($data['access_token']));
|
||||
update_site_option('wpgancio_refresh', sanitize_key($data['refresh_token']));
|
||||
add_settings_error('wpgancio_messages', 'wpgancio_messages', 'Association completed!', 'success');
|
||||
settings_errors( 'wpgancio_messages' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||
<form action="edit.php?action=wpgancio" method="post">
|
||||
<?php
|
||||
|
||||
// output security fields for the registered setting "wpgancio"
|
||||
settings_fields('wpgancio');
|
||||
|
||||
// output setting sections and their fields
|
||||
// (sections are registered for "wpgancio", each field is registered to a specific section)
|
||||
do_settings_sections('wpgancio');
|
||||
|
||||
// output save settings button
|
||||
submit_button('Save Settings');
|
||||
?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
|
@ -8,7 +8,7 @@ add_action('wp_trash_post', 'wpgancio_delete_post', 15);
|
|||
|
||||
function wpgancio_delete_post ($post_id) {
|
||||
$post = get_post($post_id);
|
||||
$instance_url = get_option('wpgancio_instance_url');
|
||||
$instance_url = get_option('wpgancio_instance_url', get_site_option('wpgancio_instance_url'));
|
||||
|
||||
if ($post->post_type == 'event') {
|
||||
$gancio_id = get_post_meta($post_id, 'wpgancio_gancio_id', TRUE);
|
||||
|
@ -17,7 +17,7 @@ function wpgancio_delete_post ($post_id) {
|
|||
$http->request( "${instance_url}/api/event/${gancio_id}", array(
|
||||
'method' => 'DELETE',
|
||||
'headers' => array (
|
||||
'Authorization' => 'Bearer ' . get_option('wpgancio_token')
|
||||
'Authorization' => 'Bearer ' . get_option('wpgancio_token', get_site_option('wpgancio_token'))
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,8 @@ function wpgancio_save_event ($post_id) {
|
|||
$venue_id = eo_get_venue($post_id);
|
||||
$place_name = eo_get_venue_name($venue_id);
|
||||
$place_address = eo_get_venue_address($venue_id);
|
||||
$instance_url = get_option('wpgancio_instance_url');
|
||||
$instance_url = get_option('wpgancio_instance_url', get_site_option('wpgancio_instance_url'));
|
||||
|
||||
|
||||
$body = array (
|
||||
'title' => $event->post_title,
|
||||
|
@ -56,7 +57,7 @@ function wpgancio_save_event ($post_id) {
|
|||
'description' => $event->post_content,
|
||||
'start_datetime' => intval($date),
|
||||
'place_name' => $place_name,
|
||||
'place_address' => "${place_address['address']}${place_address['city']}"
|
||||
'place_address' => "${place_address['address']}, ${place_address['city']}"
|
||||
);
|
||||
|
||||
// add image if specified
|
||||
|
@ -72,13 +73,13 @@ function wpgancio_save_event ($post_id) {
|
|||
$response = $http->request( $instance_url . '/api/event', array(
|
||||
'method' => 'PUT',
|
||||
'headers' => array (
|
||||
'Authorization' => 'Bearer ' . get_option('wpgancio_token'),
|
||||
'Authorization' => 'Bearer ' . get_option('wpgancio_token', get_site_option('wpgancio_token')),
|
||||
'Content-Type' => 'application/json'
|
||||
), 'body' => wp_json_encode($body) ));
|
||||
} else { // or create
|
||||
$response = wp_remote_post($instance_url . '/api/event', array(
|
||||
'headers' => array (
|
||||
'Authorization' => 'Bearer ' . get_option('wpgancio_token'),
|
||||
'Authorization' => 'Bearer ' . get_option('wpgancio_token', get_site_option('wpgancio_token')),
|
||||
'Content-Type' => 'application/json'
|
||||
), 'body' => wp_json_encode($body) ));
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ Contributors: lesion
|
|||
Donate link: https://gancio.org
|
||||
Tags: events, gancio, fediverse, AP, activity pub
|
||||
Requires at least: 4.7
|
||||
Tested up to: 5.9
|
||||
Tested up to: 6.0
|
||||
Stable tag: 1.4
|
||||
Requires PHP: 7.0
|
||||
License: AGPLv3 or later
|
||||
|
|
|
@ -3,23 +3,43 @@ defined( 'ABSPATH' ) or die( 'Nope, not accessing this' );
|
|||
// https://codex.wordpress.org/Settings_API
|
||||
|
||||
// Fires as an admin screen or script is being initialized. Register out settings
|
||||
add_action( 'admin_init', 'wpgancio_settings_init' );
|
||||
function wpgancio_settings_init() {
|
||||
if (is_network_admin()) {
|
||||
add_action('network_admin_menu', 'wpgancio_settings_init');
|
||||
} else {
|
||||
add_action('admin_menu', 'wpgancio_settings_init');
|
||||
}
|
||||
|
||||
add_action('add_meta_boxes_event', 'wpgancio_remove_meta_boxes', 10, 2);
|
||||
function wpgancio_remove_meta_boxes () {
|
||||
remove_meta_box('postcustom', 'event', 'normal');
|
||||
}
|
||||
|
||||
function wpgancio_settings_init() {
|
||||
|
||||
// register a new settings page
|
||||
add_settings_section('wpgancio_settings', __('Settings'), FALSE, 'wpgancio');
|
||||
add_settings_section('wpgancio_settings', __('Settings'), false, 'wpgancio');
|
||||
|
||||
// register a new field in the 'wpgancio_settings' section
|
||||
add_settings_field('wpgancio_instance_url', __( 'Instance URL', 'wpgancio' ),
|
||||
add_settings_field('wpgancio_instance_url',
|
||||
__('Instance URL', 'wpgancio'),
|
||||
'wpgancio_instance_url_cb', 'wpgancio',
|
||||
'wpgancio_settings');
|
||||
'wpgancio_settings'
|
||||
);
|
||||
|
||||
register_setting( 'wpgancio', 'wpgancio_instance_url', 'wpgancio_instance_url_validate' );
|
||||
register_setting('wpgancio', 'wpgancio_instance_url', 'wpgancio_instance_url_validate');
|
||||
register_setting('wpgancio', 'wpgancio_client_id');
|
||||
register_setting('wpgancio', 'wpgancio_client_secret');
|
||||
register_setting('wpgancio', 'wpgancio_token');
|
||||
}
|
||||
|
||||
|
||||
add_action( 'update_option_wpgancio_instance_url', 'wpgancio_update_options', 15, 2);
|
||||
function wpgancio_update_options ($old_value, $instance_url) {
|
||||
$redirect_uri = get_site_url(null, '/wp-admin/options-general.php?page=wpgancio' );
|
||||
if (!is_network_admin()) {
|
||||
$redirect_uri = admin_url('options-general.php?page=wpgancio');
|
||||
} else {
|
||||
$redirect_uri = network_admin_url('settings.php?page=wpgancio');
|
||||
}
|
||||
$query = join('&', array(
|
||||
'response_type=code',
|
||||
'redirect_uri=' . esc_url($redirect_uri),
|
||||
|
@ -27,18 +47,30 @@ function wpgancio_update_options ($old_value, $instance_url) {
|
|||
'client_id=' . get_option('wpgancio_client_id'),
|
||||
));
|
||||
|
||||
wp_redirect("${instance_url}/authorize?${query}");
|
||||
wp_redirect("${instance_url}/oauth/authorize?${query}");
|
||||
// return $instance_url;
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
// Fires before the administration menu loads in the admin, add our options page
|
||||
add_action( 'admin_menu', 'wpgancio_options_page' );
|
||||
add_action('admin_menu', 'wpgancio_options_page');
|
||||
|
||||
function wpgancio_instance_url_validate ($instance_url) {
|
||||
$redirect_uri = get_site_url(null, '/wp-admin/options-general.php?page=wpgancio' );
|
||||
|
||||
$old_instance_url = get_option('wpgancio_instance_url');
|
||||
if ($instance_url === $old_instance_url) {
|
||||
return $instance_url;
|
||||
}
|
||||
|
||||
if (!is_network_admin()) {
|
||||
$redirect_uri = get_site_url(null, '/wp-admin/options-general.php?page=wpgancio');
|
||||
} else {
|
||||
$redirect_uri = get_site_url(null, '/wp-admin/network/settings.php?page=wpgancio');
|
||||
}
|
||||
|
||||
// create this WP instance as a new client in selected gancio instance
|
||||
$response = wp_remote_post( "$instance_url/api/client", array(
|
||||
$response = wp_remote_post("$instance_url/api/client", array(
|
||||
'method' => 'POST',
|
||||
'body' => array(
|
||||
'client_name' => 'WPGancio',
|
||||
|
@ -48,13 +80,18 @@ function wpgancio_instance_url_validate ($instance_url) {
|
|||
)
|
||||
));
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
if (is_wp_error($response)) {
|
||||
add_settings_error('wpgancio_messages', 'wpgancio_messages',
|
||||
$response->get_error_message());
|
||||
} else {
|
||||
$data = json_decode( wp_remote_retrieve_body($response), true);
|
||||
update_option('wpgancio_client_secret', sanitize_key($data['client_secret']));
|
||||
update_option('wpgancio_client_id', sanitize_key($data['client_id']));
|
||||
if (!is_network_admin()) {
|
||||
update_option('wpgancio_client_secret', sanitize_key($data['client_secret']));
|
||||
update_option('wpgancio_client_id', sanitize_key($data['client_id']));
|
||||
} else {
|
||||
update_site_option('wpgancio_client_secret', sanitize_key($data['client_secret']));
|
||||
update_site_option('wpgancio_client_id', sanitize_key($data['client_id']));
|
||||
}
|
||||
return $instance_url;
|
||||
}
|
||||
}
|
||||
|
@ -79,12 +116,14 @@ function wpgancio_options_page() {
|
|||
// you can add custom key value pairs to be used inside your callbacks.
|
||||
function wpgancio_instance_url_cb( $args ) {
|
||||
// get the value of the setting we've registered with register_setting()
|
||||
$instance_url = get_option( 'wpgancio_instance_url' );
|
||||
if (empty($instance_url)) {
|
||||
$instance_url = WP_GANCIO_DEFAULT_INSTANCEURL;
|
||||
if (is_network_admin()) {
|
||||
$instance_url = get_site_option( 'wpgancio_instance_url' );
|
||||
} else {
|
||||
$instance_url = get_option( 'wpgancio_instance_url' );
|
||||
}
|
||||
// output the field
|
||||
?>
|
||||
|
||||
// output the field
|
||||
?>
|
||||
|
||||
<input id="wpgancio_instance_url"
|
||||
value="<?php echo esc_attr($instance_url); ?>"
|
||||
|
@ -104,13 +143,13 @@ function wpgancio_instance_url_cb( $args ) {
|
|||
*/
|
||||
function wpgancio_options_page_html() {
|
||||
// check user capabilities
|
||||
if ( ! current_user_can( 'manage_options' ) ) { return; }
|
||||
if (! current_user_can('manage_options')) { return; }
|
||||
|
||||
// show error/update messages
|
||||
$code = sanitize_key($_GET['code']);
|
||||
$code = sanitize_key(isset($_GET['code']) ? $_GET['code'] : '');
|
||||
if ( $code ) {
|
||||
update_option('wpgancio_code', $code);
|
||||
$instance_url = get_option( 'wpgancio_instance_url' );
|
||||
$instance_url = get_option('wpgancio_instance_url');
|
||||
|
||||
$response = wp_remote_post($instance_url . "/oauth/token", array(
|
||||
'body' => array(
|
||||
|
@ -120,18 +159,18 @@ function wpgancio_options_page_html() {
|
|||
'grant_type' => 'authorization_code',
|
||||
'code' => $code
|
||||
)));
|
||||
if ( is_wp_error( $response ) ) {
|
||||
if (is_wp_error($response)) {
|
||||
add_settings_error('wpgancio_messages', 'wpgancio_messages', $response->get_error_message());
|
||||
settings_errors( 'wpgancio_messages' );
|
||||
} else if ( $response['response']['code'] == 500 ) {
|
||||
settings_errors('wpgancio_messages');
|
||||
} elseif ($response['response']['code'] == 500) {
|
||||
add_settings_error('wpgancio_messages', 'wpgancio_messages', wp_remote_retrieve_body($response));
|
||||
settings_errors( 'wpgancio_messages' );
|
||||
settings_errors('wpgancio_messages');
|
||||
} else {
|
||||
$data = json_decode( wp_remote_retrieve_body($response), true);
|
||||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
update_option('wpgancio_token', sanitize_key($data['access_token']));
|
||||
update_option('wpgancio_refresh', sanitize_key($data['refresh_token']));
|
||||
add_settings_error('wpgancio_messages', 'wpgancio_messages', 'Association completed!', 'success');
|
||||
settings_errors( 'wpgancio_messages' );
|
||||
settings_errors('wpgancio_messages');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,19 +178,20 @@ function wpgancio_options_page_html() {
|
|||
?>
|
||||
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
|
||||
|
||||
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||
<form action="options.php" method="post">
|
||||
<?php
|
||||
|
||||
// output security fields for the registered setting "wpgancio"
|
||||
settings_fields( 'wpgancio' );
|
||||
settings_fields('wpgancio');
|
||||
|
||||
// output setting sections and their fields
|
||||
// (sections are registered for "wpgancio", each field is registered to a specific section)
|
||||
do_settings_sections( 'wpgancio' );
|
||||
do_settings_sections('wpgancio');
|
||||
|
||||
// output save settings button
|
||||
submit_button( 'Save Settings' );
|
||||
submit_button('Save Settings');
|
||||
?>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -39,10 +39,10 @@ function gancio_events_handler_function( $atts, $content, $tag) {
|
|||
'places' => '',
|
||||
'tags' => '',
|
||||
'theme' => 'dark',
|
||||
'max' => NULL
|
||||
'max' => null
|
||||
), $atts);
|
||||
return '<gancio-events baseurl="' . $a['baseurl'] . '" theme="' . $a['theme'] . '" places="' . $a['places'] . '" tags="' . $a['tags'] . '"></gancio-events>';
|
||||
};
|
||||
}
|
||||
|
||||
add_action( 'init', function () {
|
||||
global $allowedposttags;
|
||||
|
|
256
yarn.lock
256
yarn.lock
|
@ -2,11 +2,6 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@alloc/quick-lru@^5.2.0":
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
|
||||
integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
|
||||
|
||||
"@ampproject/remapping@^2.1.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
|
||||
|
@ -1954,7 +1949,7 @@
|
|||
dependencies:
|
||||
"@types/ms" "*"
|
||||
|
||||
"@types/geojson@^7946.0.8":
|
||||
"@types/geojson@^7946.0.10":
|
||||
version "7946.0.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249"
|
||||
integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==
|
||||
|
@ -2027,7 +2022,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
|
||||
integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==
|
||||
|
||||
"@types/node@^17.0.10":
|
||||
"@types/node@^17.0.45":
|
||||
version "17.0.45"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190"
|
||||
integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==
|
||||
|
@ -2950,11 +2945,6 @@ base@^0.11.1:
|
|||
mixin-deep "^1.2.0"
|
||||
pascalcase "^0.1.1"
|
||||
|
||||
basic-auth@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-1.1.0.tgz#45221ee429f7ee1e5035be3f51533f1cdfd29884"
|
||||
integrity sha512-CtGuTyWf3ig+sgRyC7uP6DM3N+5ur/p8L+FPfsd+BbIfIs74TFfCajZTHnCw6K5dqM0bZEbRIqRy1fAdiUJhTA==
|
||||
|
||||
bcp47@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/bcp47/-/bcp47-1.1.2.tgz#354be3307ffd08433a78f5e1e2095845f89fc7fe"
|
||||
|
@ -3008,16 +2998,6 @@ bluebird@3.4.x:
|
|||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3"
|
||||
integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==
|
||||
|
||||
bluebird@3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
|
||||
integrity sha512-3LE8m8bqjGdoxfvf71yhFNrUcwy3NLy00SAo+b6MfJ8l+Bc2DzQ7mUHwX6pjK2AxfgV+YfsjCeVW3T5HLQTBsQ==
|
||||
|
||||
bluebird@^2.10.0:
|
||||
version "2.11.0"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
|
||||
integrity sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==
|
||||
|
||||
bluebird@^3.1.1, bluebird@^3.5.5, bluebird@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||
|
@ -3680,19 +3660,6 @@ clone-deep@^4.0.1:
|
|||
kind-of "^6.0.2"
|
||||
shallow-clone "^3.0.0"
|
||||
|
||||
co-bluebird@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/co-bluebird/-/co-bluebird-1.1.0.tgz#c8b9f3a9320a7ed30987dcca1a5c3cff59655c7c"
|
||||
integrity sha512-JuoemMXxQjYAxbfRrNpOsLyiwDiY8mXvGqJyYLM7jMySDJtnMklW3V2o8uyubpc1eN2YoRsAdfZ1lfKCd3lsrA==
|
||||
dependencies:
|
||||
bluebird "^2.10.0"
|
||||
co-use "^1.1.0"
|
||||
|
||||
co-use@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/co-use/-/co-use-1.1.0.tgz#c6bb3cdf10cb735ecaa9daeeda46d725c94a4e62"
|
||||
integrity sha512-1lVRtdywv41zQO/xvI2wU8w6oFcUYT6T84YKSxN25KN4N4Kld3scLovt8FjDmD63Cm7HtyRWHjezt+IanXmkyA==
|
||||
|
||||
co@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||
|
@ -3950,6 +3917,11 @@ cookie@0.4.1:
|
|||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
|
||||
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
|
||||
|
||||
cookie@0.4.2, cookie@^0.4.0, cookie@^0.4.1:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
|
||||
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
|
||||
|
||||
cookie@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
|
@ -3960,11 +3932,6 @@ cookie@^0.3.1:
|
|||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
|
||||
integrity sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==
|
||||
|
||||
cookie@^0.4.0, cookie@^0.4.1:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
|
||||
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
|
||||
|
||||
cookiejar@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc"
|
||||
|
@ -4398,7 +4365,7 @@ de-indent@^1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
||||
integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
|
||||
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
|
||||
debug@2.6.9, debug@2.x.x, debug@^2.2.0, debug@^2.3.3:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
|
@ -4513,12 +4480,12 @@ delegates@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
||||
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
|
||||
|
||||
denque@^2.0.1:
|
||||
denque@^2.0.1, denque@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
|
||||
integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
|
||||
|
||||
depd@2.0.0:
|
||||
depd@2.0.0, depd@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
||||
|
@ -5199,11 +5166,19 @@ expect@^29.2.2:
|
|||
jest-message-util "^29.2.1"
|
||||
jest-util "^29.2.1"
|
||||
|
||||
express-oauth-server@lesion/express-oauth-server#master:
|
||||
version "2.0.0"
|
||||
resolved "https://codeload.github.com/lesion/express-oauth-server/tar.gz/711002b355e1a7e7e016ff1ed616ecdb184a2fe0"
|
||||
express-session@^1.17.3:
|
||||
version "1.17.3"
|
||||
resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.17.3.tgz#14b997a15ed43e5949cb1d073725675dd2777f36"
|
||||
integrity sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==
|
||||
dependencies:
|
||||
oauth2-server "3.0.0"
|
||||
cookie "0.4.2"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "~2.0.0"
|
||||
on-headers "~1.0.2"
|
||||
parseurl "~1.3.3"
|
||||
safe-buffer "5.2.1"
|
||||
uid-safe "~2.1.5"
|
||||
|
||||
express@^4.18.1:
|
||||
version "4.18.2"
|
||||
|
@ -6637,11 +6612,6 @@ is-generator-fn@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
|
||||
integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
|
||||
|
||||
is-generator@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-generator/-/is-generator-1.0.3.tgz#c14c21057ed36e328db80347966c693f886389f3"
|
||||
integrity sha512-G56jBpbJeg7ds83HW1LuShNs8J73Fv3CPz/bmROHOHlnKkN8sWb9ujiagjmxxMUywftgq48HlBZELKKqFLk0oA==
|
||||
|
||||
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
|
||||
|
@ -7293,9 +7263,9 @@ jsbn@~0.1.0:
|
|||
integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==
|
||||
|
||||
jsdom@^20.0.0:
|
||||
version "20.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.1.tgz#d95b4a3b6e1eec6520aa01d9d908eade8c6ba153"
|
||||
integrity sha512-pksjj7Rqoa+wdpkKcLzQRHhJCEE42qQhl/xLMUKHgoSejaKOdaXEAnqs6uDNwMl/fciHTzKeR8Wm8cw7N+g98A==
|
||||
version "20.0.2"
|
||||
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.2.tgz#65ccbed81d5e877c433f353c58bb91ff374127db"
|
||||
integrity sha512-AHWa+QO/cgRg4N+DsmHg1Y7xnz+8KU3EflM0LVDTdmrYOc1WWTSkOjtpUveQH+1Bqd5rtcVnb/DuxV/UjDO4rA==
|
||||
dependencies:
|
||||
abab "^2.0.6"
|
||||
acorn "^8.8.0"
|
||||
|
@ -7754,7 +7724,7 @@ lodash.uniq@^4.5.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
|
||||
|
||||
lodash@4.17.4, lodash@^4.15.0, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5:
|
||||
lodash@^4.15.0, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
@ -7817,6 +7787,11 @@ lru-cache@^6.0.0:
|
|||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
lru-cache@^7.14.0:
|
||||
version "7.14.1"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.14.1.tgz#8da8d2f5f59827edb388e63e459ac23d6d408fea"
|
||||
integrity sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==
|
||||
|
||||
lru-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3"
|
||||
|
@ -7917,17 +7892,16 @@ map-visit@^1.0.0:
|
|||
object-visit "^1.0.0"
|
||||
|
||||
mariadb@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mariadb/-/mariadb-3.0.1.tgz#af9aa0aeffd549be171b4b41bdd04315fca13817"
|
||||
integrity sha512-orYg4JojWszNWlI1a+We2TUe4h+S5WIkjUa0U4DF0jjrOtxBVtCsQtioXxURWCxVfxwQ5wZVZkBoa53lqRuQeQ==
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/mariadb/-/mariadb-3.0.2.tgz#427ae286b8fb35f4046b3457f4df729a5019d6c3"
|
||||
integrity sha512-dVjiQZ6RW0IXFnX+T/ZEmnqs724DgkQsXqfCyInXn0XxVfO2Px6KbS4M3Ny6UiBg0zJ93SHHvfVBgYO4ZnFvvw==
|
||||
dependencies:
|
||||
"@alloc/quick-lru" "^5.2.0"
|
||||
"@types/geojson" "^7946.0.8"
|
||||
"@types/node" "^17.0.10"
|
||||
denque "^2.0.1"
|
||||
"@types/geojson" "^7946.0.10"
|
||||
"@types/node" "^17.0.45"
|
||||
denque "^2.1.0"
|
||||
iconv-lite "^0.6.3"
|
||||
moment-timezone "^0.5.34"
|
||||
please-upgrade-node "^3.2.0"
|
||||
lru-cache "^7.14.0"
|
||||
moment-timezone "^0.5.38"
|
||||
|
||||
math-interval-parser@^2.0.1:
|
||||
version "2.0.1"
|
||||
|
@ -8082,7 +8056,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
|
|||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||
|
||||
mime-types@^2.1.12, mime-types@^2.1.19, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.15, mime-types@~2.1.24, mime-types@~2.1.34:
|
||||
mime-types@^2.1.12, mime-types@^2.1.19, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.24, mime-types@~2.1.34:
|
||||
version "2.1.35"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||
|
@ -8243,7 +8217,7 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
moment-timezone@^0.5.34:
|
||||
moment-timezone@^0.5.34, moment-timezone@^0.5.38:
|
||||
version "0.5.38"
|
||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.38.tgz#9674a5397b8be7c13de820fd387d8afa0f725aad"
|
||||
integrity sha512-nMIrzGah4+oYZPflDvLZUgoVUO4fvAqHstvG3xAUnMolWncuAiLDWNnJZj6EwJGMGfb1ZcuTFE6GI3hNOVWI/Q==
|
||||
|
@ -8723,17 +8697,14 @@ nwsapi@^2.2.2:
|
|||
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0"
|
||||
integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==
|
||||
|
||||
oauth2-server@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/oauth2-server/-/oauth2-server-3.0.0.tgz#c46276b74c3d28634d59ee981f76b58a6459cc28"
|
||||
integrity sha512-TlDDkKECOTjQQ9pQobw/EESLbd7YVY1i0Ebos996Au88FqiLUbQ+X/cRBCq6gvpkoA0ByrDsF8c97SyRygfE6Q==
|
||||
oauth2orize@^1.11.1:
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/oauth2orize/-/oauth2orize-1.11.1.tgz#00b6cafe2036a0a3aab0380627dc7cfd5b5e9a9c"
|
||||
integrity sha512-9dSx/Gwm0J2Rvj4RH9+h7iXVnRXZ6biwWRgb2dCeQhCosODS0nYdM9I/G7BUGsjbgn0pHjGcn1zcCRtzj2SlRA==
|
||||
dependencies:
|
||||
basic-auth "1.1.0"
|
||||
bluebird "3.5.0"
|
||||
lodash "4.17.4"
|
||||
promisify-any "2.0.1"
|
||||
statuses "1.3.1"
|
||||
type-is "1.6.15"
|
||||
debug "2.x.x"
|
||||
uid2 "0.0.x"
|
||||
utils-merge "1.x.x"
|
||||
|
||||
object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
|
@ -9102,6 +9073,62 @@ pascalcase@^0.1.1:
|
|||
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
|
||||
integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==
|
||||
|
||||
passport-anonymous@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/passport-anonymous/-/passport-anonymous-1.0.1.tgz#241e37274ec44dfb7f6cad234b41c438386bc117"
|
||||
integrity sha512-Mk2dls97nLTzHpsWCYQ54IVGucWaiWSHHr3+IhWYAebg4dRgRQIfyoeYrixoxB2z2z4+EM7p9yjC+a3yMB5z5A==
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
|
||||
passport-custom@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/passport-custom/-/passport-custom-1.1.1.tgz#71db3d7ec1d7d0085e8768507f61b26d88051c0a"
|
||||
integrity sha512-/2m7jUGxmCYvoqenLB9UrmkCgPt64h8ZtV+UtuQklZ/Tn1NpKBeOorCYkB/8lMRoiZ5hUrCoMmDtxCS/d38mlg==
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
|
||||
passport-http-bearer@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/passport-http-bearer/-/passport-http-bearer-1.0.1.tgz#147469ea3669e2a84c6167ef99dbb77e1f0098a8"
|
||||
integrity sha512-SELQM+dOTuMigr9yu8Wo4Fm3ciFfkMq5h/ZQ8ffi4ELgZrX1xh9PlglqZdcUZ1upzJD/whVyt+YWF62s3U6Ipw==
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
|
||||
passport-http@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-http/-/passport-http-0.3.0.tgz#8ee53d4380be9c60df2151925029826f77115603"
|
||||
integrity sha512-OwK9DkqGVlJfO8oD0Bz1VDIo+ijD3c1ZbGGozIZw+joIP0U60pXY7goB+8wiDWtNqHpkTaQiJ9Ux1jE3Ykmpuw==
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
|
||||
passport-oauth2-client-password@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/passport-oauth2-client-password/-/passport-oauth2-client-password-0.1.2.tgz#4f378b678b92d16dbbd233a6c706520093e561ba"
|
||||
integrity sha512-GHQH4UtaEZvCLulAxGKHYoSsPRoPRmGsdmaZtMh5nmz80yMLQbdMA9Bg2sp4/UW3PIxJH/143hVjPTiXaNngTQ==
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
|
||||
passport-oauth2-client-public@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/passport-oauth2-client-public/-/passport-oauth2-client-public-0.0.1.tgz#f193d55b5eb4c6080f9465992046c31255e3a0eb"
|
||||
integrity sha512-VN7R+QX7XT5ZZQutOxwzxPIwaANb+T1mdKHtexQnDN8MsrhRVYS9bqN9bcmXGA4QsDR8isCjsfBATfM9L5maZw==
|
||||
dependencies:
|
||||
passport-strategy "^1.0.0"
|
||||
|
||||
passport-strategy@1.x.x, passport-strategy@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
|
||||
integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==
|
||||
|
||||
passport@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/passport/-/passport-0.6.0.tgz#e869579fab465b5c0b291e841e6cc95c005fac9d"
|
||||
integrity sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
pause "0.0.1"
|
||||
utils-merge "^1.0.1"
|
||||
|
||||
path-browserify@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
|
||||
|
@ -9147,6 +9174,11 @@ path-type@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||
|
||||
pause@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d"
|
||||
integrity sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==
|
||||
|
||||
pbkdf2@^3.0.3:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
|
||||
|
@ -9263,13 +9295,6 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0:
|
|||
dependencies:
|
||||
find-up "^4.0.0"
|
||||
|
||||
please-upgrade-node@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
|
||||
integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==
|
||||
dependencies:
|
||||
semver-compare "^1.0.0"
|
||||
|
||||
pnp-webpack-plugin@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.7.0.tgz#65741384f6d8056f36e2255a8d67ffc20866f5c9"
|
||||
|
@ -10065,15 +10090,6 @@ promise@^7.0.1:
|
|||
dependencies:
|
||||
asap "~2.0.3"
|
||||
|
||||
promisify-any@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/promisify-any/-/promisify-any-2.0.1.tgz#403e00a8813f175242ab50fe33a69f8eece47305"
|
||||
integrity sha512-pVaGouFbTVxqpVJ+T5A15olNJDASAZHYq5cXz6mWdr6/X34mVWiG9MSdzHTcVBCv4aqBP7wGspi7BUSRbEmhsw==
|
||||
dependencies:
|
||||
bluebird "^2.10.0"
|
||||
co-bluebird "^1.1.0"
|
||||
is-generator "^1.0.2"
|
||||
|
||||
prompts@^2.0.1:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
|
||||
|
@ -10482,6 +10498,11 @@ randexp@0.4.6:
|
|||
discontinuous-range "1.0.0"
|
||||
ret "~0.1.10"
|
||||
|
||||
random-bytes@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
|
||||
integrity sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==
|
||||
|
||||
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||
|
@ -10979,11 +11000,6 @@ selderee@^0.6.0:
|
|||
dependencies:
|
||||
parseley "^0.7.0"
|
||||
|
||||
semver-compare@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
||||
integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
|
||||
|
||||
semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
|
@ -11524,11 +11540,6 @@ static-extend@^0.1.1:
|
|||
define-property "^0.2.5"
|
||||
object-copy "^0.1.0"
|
||||
|
||||
statuses@1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
|
||||
integrity sha512-wuTCPGlJONk/a1kqZ4fQM2+908lC7fa7nPYpTC1EhnvqLX/IICbeP1OZGDtA374trpSq68YubKUMo8oRhN46yg==
|
||||
|
||||
statuses@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
|
||||
|
@ -12187,14 +12198,6 @@ type-fest@^0.8.1:
|
|||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
|
||||
|
||||
type-is@1.6.15:
|
||||
version "1.6.15"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410"
|
||||
integrity sha512-0uqZYZDiBICTVXEsNcDLueZLPgZ8FgGe8lmVDQ0FcVFUeaxsPbFWiz60ZChVw8VELIt7iGuCehOrZSYjYteWKQ==
|
||||
dependencies:
|
||||
media-typer "0.3.0"
|
||||
mime-types "~2.1.15"
|
||||
|
||||
type-is@^1.6.4, type-is@~1.6.18:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
|
@ -12248,6 +12251,18 @@ uglify-js@^3.5.1:
|
|||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.2.tgz#f55f668b9a64b213977ae688703b6bbb7ca861c6"
|
||||
integrity sha512-bbxglRjsGQMchfvXZNusUcYgiB9Hx2K4AHYXQy2DITZ9Rd+JzhX7+hoocE5Winr7z2oHvPsekkBwXtigvxevXg==
|
||||
|
||||
uid-safe@~2.1.5:
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a"
|
||||
integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==
|
||||
dependencies:
|
||||
random-bytes "~1.0.0"
|
||||
|
||||
uid2@0.0.x:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.4.tgz#033f3b1d5d32505f5ce5f888b9f3b667123c0a44"
|
||||
integrity sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==
|
||||
|
||||
umzug@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/umzug/-/umzug-2.3.0.tgz#0ef42b62df54e216b05dcaf627830a6a8b84a184"
|
||||
|
@ -12482,7 +12497,7 @@ utila@~0.4:
|
|||
resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
|
||||
integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==
|
||||
|
||||
utils-merge@1.0.1:
|
||||
utils-merge@1.0.1, utils-merge@1.x.x, utils-merge@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
||||
|
@ -13086,7 +13101,7 @@ yargs-parser@^20.2.2:
|
|||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs-parser@^21.0.0:
|
||||
yargs-parser@^21.0.0, yargs-parser@^21.1.1:
|
||||
version "21.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
|
||||
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
|
||||
|
@ -13104,7 +13119,7 @@ yargs@^16.2.0:
|
|||
y18n "^5.0.5"
|
||||
yargs-parser "^20.2.2"
|
||||
|
||||
yargs@^17.3.1, yargs@^17.5.0:
|
||||
yargs@^17.3.1:
|
||||
version "17.6.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.0.tgz#e134900fc1f218bc230192bdec06a0a5f973e46c"
|
||||
integrity sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==
|
||||
|
@ -13117,6 +13132,19 @@ yargs@^17.3.1, yargs@^17.5.0:
|
|||
y18n "^5.0.5"
|
||||
yargs-parser "^21.0.0"
|
||||
|
||||
yargs@^17.5.0:
|
||||
version "17.6.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541"
|
||||
integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==
|
||||
dependencies:
|
||||
cliui "^8.0.1"
|
||||
escalade "^3.1.1"
|
||||
get-caller-file "^2.0.5"
|
||||
require-directory "^2.1.1"
|
||||
string-width "^4.2.3"
|
||||
y18n "^5.0.5"
|
||||
yargs-parser "^21.1.1"
|
||||
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
|
|
Loading…
Reference in a new issue