show oauth2 authorizations on user settings page
This commit is contained in:
parent
27668e8f2e
commit
9b7bc571e0
@ -297,6 +297,47 @@ const API = {
|
||||
|
||||
return {error: null, user: user}
|
||||
}
|
||||
},
|
||||
OAuth2: {
|
||||
getUserAuthorizations: async function (user) {
|
||||
user = await API.User.ensureObject(user)
|
||||
let auths = await models.OAuth2AuthorizedClient.query().where('user_id', user.id)
|
||||
|
||||
let nicelist = []
|
||||
|
||||
for (let i in auths) {
|
||||
let auth = auths[i]
|
||||
let client = await models.OAuth2Client.query().where('id', auth.client_id)
|
||||
|
||||
if (!client.length) continue
|
||||
client = client[0]
|
||||
|
||||
let obj = {
|
||||
id: client.id,
|
||||
title: client.title,
|
||||
description: client.description,
|
||||
url: client.url,
|
||||
icon: client.icon,
|
||||
scope: client.scope.split(' '),
|
||||
created_at: auth.created_at,
|
||||
expires_at: auth.expires_at
|
||||
}
|
||||
nicelist.push(obj)
|
||||
}
|
||||
|
||||
return nicelist
|
||||
},
|
||||
removeUserAuthorization: async function (user, clientId) {
|
||||
user = await API.User.ensureObject(user)
|
||||
let auth = await models.OAuth2AuthorizedClient.query().where('user_id', user.id).andWhere('client_id', clientId)
|
||||
if (!auth.length) return false
|
||||
|
||||
for (let i in auth) {
|
||||
await models.OAuth2AuthorizedClient.query().delete().where('id', auth[i].id)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import News from '../api/news'
|
||||
import Image from '../api/image'
|
||||
import APIExtern from '../api/external'
|
||||
|
||||
const userContent = path.join(__dirname, '../..', 'usercontent')
|
||||
// const userContent = path.join(__dirname, '../..', 'usercontent')
|
||||
|
||||
let router = express.Router()
|
||||
|
||||
@ -289,6 +289,11 @@ router.get('/news', wrap(async (req, res) => {
|
||||
res.jsonp(articles)
|
||||
}))
|
||||
|
||||
/* ==========
|
||||
* AVATAR
|
||||
* ==========
|
||||
*/
|
||||
// Promisify multiparty form parser
|
||||
async function promiseForm (req) {
|
||||
let form = new multiparty.Form()
|
||||
return new Promise(function (resolve, reject) {
|
||||
@ -299,6 +304,7 @@ async function promiseForm (req) {
|
||||
})
|
||||
}
|
||||
|
||||
// Upload avatar image
|
||||
router.post('/avatar', uploadLimiter, wrap(async (req, res, next) => {
|
||||
if (!req.session.user) return next()
|
||||
let data = await promiseForm(req)
|
||||
@ -320,6 +326,7 @@ router.post('/avatar', uploadLimiter, wrap(async (req, res, next) => {
|
||||
res.status(200).jsonp({})
|
||||
}))
|
||||
|
||||
// Remove avatar image
|
||||
router.post('/avatar/remove', wrap(async (req, res, next) => {
|
||||
if (!req.session.user) return next()
|
||||
|
||||
@ -329,6 +336,7 @@ router.post('/avatar/remove', wrap(async (req, res, next) => {
|
||||
res.status(200).jsonp({done: true})
|
||||
}))
|
||||
|
||||
// Get latest avatar of logged in user
|
||||
router.get('/avatar', wrap(async (req, res, next) => {
|
||||
if (!req.session.user) return next()
|
||||
let user = req.session.user
|
||||
@ -339,6 +347,7 @@ router.get('/avatar', wrap(async (req, res, next) => {
|
||||
res.redirect('/usercontent/images/' + user.avatar_file)
|
||||
}))
|
||||
|
||||
// Get latest avatar of user by id
|
||||
router.get('/avatar/:id', wrap(async (req, res, next) => {
|
||||
let id = parseInt(req.params.id)
|
||||
if (isNaN(id)) return next()
|
||||
@ -351,10 +360,37 @@ router.get('/avatar/:id', wrap(async (req, res, next) => {
|
||||
res.redirect('/usercontent/images/' + user.avatar_file)
|
||||
}))
|
||||
|
||||
// Redirect to no avatar on 404
|
||||
router.use('/avatar', (req, res) => {
|
||||
res.redirect('/static/image/avatar.png')
|
||||
})
|
||||
|
||||
/* =====================
|
||||
* OAuth2 Management
|
||||
* =====================
|
||||
*/
|
||||
|
||||
router.get('/oauth2/authorized-clients', wrap(async (req, res, next) => {
|
||||
if (!req.session.user) return next()
|
||||
|
||||
let list = await API.User.OAuth2.getUserAuthorizations(req.session.user)
|
||||
if (!list) return next()
|
||||
|
||||
res.jsonp(list)
|
||||
}))
|
||||
|
||||
router.post('/oauth2/authorized-clients/delete', wrap(async (req, res, next) => {
|
||||
if (!req.session.user) return next()
|
||||
|
||||
let clientId = parseInt(req.body.client_id)
|
||||
if (isNaN(clientId)) return res.status(400).jsonp({error: 'Missing Client ID parameter'})
|
||||
|
||||
let done = await API.User.OAuth2.removeUserAuthorization(req.session.user, clientId)
|
||||
if (!done) return res.status(400).jsonp({error: 'Failed to remove client authorization'})
|
||||
|
||||
res.status(204).end()
|
||||
}))
|
||||
|
||||
// 404
|
||||
router.use((req, res) => {
|
||||
res.status(404).jsonp({error: 'Not found'})
|
||||
|
@ -102,7 +102,7 @@ router.get('/login', extraButtons, (req, res) => {
|
||||
return res.redirect(uri)
|
||||
}
|
||||
|
||||
res.render('login')
|
||||
res.render('user/login')
|
||||
})
|
||||
|
||||
router.get('/register', extraButtons, (req, res) => {
|
||||
@ -121,7 +121,7 @@ router.get('/register', extraButtons, (req, res) => {
|
||||
res.locals.recaptcha = config.security.recaptcha.site_key
|
||||
}
|
||||
|
||||
res.render('register')
|
||||
res.render('user/register')
|
||||
})
|
||||
|
||||
router.get('/user/two-factor', ensureLogin, wrap(async (req, res) => {
|
||||
@ -131,18 +131,18 @@ router.get('/user/two-factor', ensureLogin, wrap(async (req, res) => {
|
||||
let newToken = await API.User.Login.totpAquire(req.session.user)
|
||||
if (!newToken) return res.redirect('/')
|
||||
|
||||
res.render('totp', { uri: newToken })
|
||||
res.render('user/totp', { uri: newToken })
|
||||
}))
|
||||
|
||||
router.get('/user/two-factor/disable', ensureLogin, wrap(async (req, res) => {
|
||||
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
||||
|
||||
if (!twoFaEnabled) return res.redirect('/')
|
||||
res.render('password')
|
||||
res.render('user/password')
|
||||
}))
|
||||
|
||||
router.get('/login/verify', (req, res) => {
|
||||
res.render('totp-check')
|
||||
res.render('user/totp-check')
|
||||
})
|
||||
|
||||
router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
|
||||
@ -177,11 +177,11 @@ router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
res.render('settings', {totp: totpEnabled, password: socialStatus.password})
|
||||
res.render('user/settings', {totp: totpEnabled, password: socialStatus.password})
|
||||
}))
|
||||
|
||||
router.get('/user/manage/password', ensureLogin, wrap(async (req, res) => {
|
||||
res.render('password_new')
|
||||
res.render('user/password_new')
|
||||
}))
|
||||
|
||||
router.get('/user/manage/email', ensureLogin, wrap(async (req, res) => {
|
||||
@ -194,7 +194,7 @@ router.get('/user/manage/email', ensureLogin, wrap(async (req, res) => {
|
||||
|
||||
let socialStatus = await API.User.socialStatus(req.session.user)
|
||||
|
||||
res.render('email_change', {email: obfuscated, password: socialStatus.password})
|
||||
res.render('user/email_change', {email: obfuscated, password: socialStatus.password})
|
||||
}))
|
||||
|
||||
/*
|
||||
@ -622,7 +622,7 @@ router.get('/news/', wrap(async (req, res) => {
|
||||
router.get('/partials/:view', wrap(async (req, res, next) => {
|
||||
if (!req.params.view) return next()
|
||||
|
||||
res.render('partials/' + req.params.view)
|
||||
res.render('user/partials/' + req.params.view)
|
||||
}))
|
||||
|
||||
/*
|
||||
|
@ -36,6 +36,61 @@ $(document).ready(function () {
|
||||
return newWindow
|
||||
}
|
||||
|
||||
function removeAuthorization (clientId) {
|
||||
$.ajax({
|
||||
type: 'post',
|
||||
url: '/api/oauth2/authorized-clients/delete',
|
||||
data: { client_id: clientId },
|
||||
success: function (data) {
|
||||
loadAuthorizations()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function loadAuthorizations () {
|
||||
$.get({
|
||||
url: '/api/oauth2/authorized-clients',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
if (!data.length) {
|
||||
return $('#clientlist').html('There is nothing to show at this moment.')
|
||||
}
|
||||
var html = ''
|
||||
for (var i in data) {
|
||||
var client = data[i]
|
||||
html += '<div class="authclient application" data-client-id="' + client.id + '" id="client-' + client.id + '">'
|
||||
html += '<div class="remove" id="deleteclient"><i class="fa fa-fw fa-ban"></i></div>'
|
||||
html += '<div class="picture">'
|
||||
|
||||
if (client.icon) {
|
||||
html += '<img src="' + client.icon + '">'
|
||||
} else {
|
||||
html += '<div class="noicon"><i class="fa fa-fw fa-gears"></i></div>'
|
||||
}
|
||||
|
||||
html += '</div>'
|
||||
html += '<div class="info">'
|
||||
html += '<div class="name">' + client.title + '</div>'
|
||||
html += '<div class="description">' + client.description + '</div>'
|
||||
html += '<a class="url" href="' + client.url + '">' + client.url + '</a>'
|
||||
html += '<div class="timestamp">Authorized ' + new Date(client.created_at) + '</div>'
|
||||
html += '</div></div>'
|
||||
}
|
||||
|
||||
$('#clientlist').html(html)
|
||||
|
||||
for (let i in data) {
|
||||
$('#client-' + data[i].id + ' #deleteclient').click(function (e) {
|
||||
let clid = $(this).parent().data('client-id')
|
||||
if (clid != null) {
|
||||
removeAuthorization(clid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
window.Dialog = $('#dialog')
|
||||
window.Dialog.open = function (title, content, pad) {
|
||||
$('#dialog #title').text(title)
|
||||
@ -152,6 +207,10 @@ $(document).ready(function () {
|
||||
})
|
||||
}
|
||||
|
||||
if ($('#clientlist').length) {
|
||||
loadAuthorizations()
|
||||
}
|
||||
|
||||
if ($('#newAvatar').length) {
|
||||
$('#newAvatar').click(function (e) {
|
||||
e.preventDefault()
|
||||
|
@ -237,6 +237,7 @@ input:not([type="submit"])
|
||||
background-color: #fff
|
||||
box-shadow: 5px 5px 15px #868686
|
||||
border: 1px solid #ddd
|
||||
margin-bottom: 10%
|
||||
h1:first-child, h2:first-child, h3:first-child
|
||||
margin-top: 0
|
||||
.left, .right
|
||||
@ -360,6 +361,11 @@ span.divider
|
||||
|
||||
.application
|
||||
height: 140px
|
||||
#deleteclient
|
||||
float: right
|
||||
color: red
|
||||
font-size: 120%
|
||||
cursor: pointer
|
||||
.picture
|
||||
width: 120px
|
||||
height: 120px
|
||||
@ -387,6 +393,7 @@ span.divider
|
||||
display: block
|
||||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
max-width: 200px
|
||||
|
||||
input.authorize
|
||||
background-color: #00b0ff
|
||||
@ -421,12 +428,13 @@ input.invalid
|
||||
margin-top: 20px
|
||||
margin-bottom: 20px
|
||||
|
||||
span.load
|
||||
font-size: 120%
|
||||
span
|
||||
vertical-align: super
|
||||
margin-left: 10px
|
||||
|
||||
.newsfeed
|
||||
.load
|
||||
font-size: 120%
|
||||
span
|
||||
vertical-align: super
|
||||
margin-left: 10px
|
||||
.prvarticle
|
||||
margin-bottom: 10px
|
||||
.title
|
||||
|
@ -1,4 +1,4 @@
|
||||
extends layout.pug
|
||||
extends ../layout.pug
|
||||
block title
|
||||
|Icy Network - Change User Email
|
||||
|
@ -1,4 +1,4 @@
|
||||
extends layout.pug
|
||||
extends ../layout.pug
|
||||
block title
|
||||
|Icy Network - Log In
|
||||
|
||||
@ -24,4 +24,4 @@ block body
|
||||
input(type="submit", value="Log in")
|
||||
a#create(href="/register") Create an account
|
||||
.right
|
||||
include includes/external.pug
|
||||
include ../includes/external.pug
|
@ -4,7 +4,7 @@
|
||||
.otherdata
|
||||
h3 Current Avatar
|
||||
.avatar
|
||||
include ../includes/avatar.pug
|
||||
include ../../includes/avatar.pug
|
||||
.inputting
|
||||
h3 Upload new
|
||||
.message.error
|
@ -1,4 +1,4 @@
|
||||
extends layout.pug
|
||||
extends ../layout.pug
|
||||
block title
|
||||
|Icy Network - Password Required
|
||||
|
@ -1,4 +1,4 @@
|
||||
extends layout.pug
|
||||
extends ../layout.pug
|
||||
block title
|
||||
|Icy Network - Change User Password
|
||||
|
@ -1,4 +1,4 @@
|
||||
extends layout.pug
|
||||
extends ../layout.pug
|
||||
block title
|
||||
|Icy Network - Register
|
||||
|
||||
@ -34,4 +34,4 @@ block body
|
||||
input(type="submit", value="Register")
|
||||
a#create(href="/login") Log in with an existing account
|
||||
.right
|
||||
include includes/external.pug
|
||||
include ../includes/external.pug
|
@ -1,4 +1,4 @@
|
||||
extends layout.pug
|
||||
extends ../layout.pug
|
||||
block title
|
||||
|Icy Network - User Settings
|
||||
|
||||
@ -23,7 +23,7 @@ block body
|
||||
input(type="text", name="display_name", id="display_name", value=user.display_name)
|
||||
label Avatar
|
||||
.avatarCont
|
||||
include includes/avatar.pug
|
||||
include ../includes/avatar.pug
|
||||
.options
|
||||
a#newAvatar(href='#') Change Avatar
|
||||
if user.avatar_file
|
||||
@ -31,7 +31,7 @@ block body
|
||||
input(type="submit", value="Save Settings")
|
||||
.right
|
||||
h3 Social Media Accounts
|
||||
include includes/external.pug
|
||||
include ../includes/external.pug
|
||||
if twitter_auth == false
|
||||
a.option.accdisconnect(href="/api/external/twitter/remove")
|
||||
i.fa.fa-fw.fa-times
|
||||
@ -60,3 +60,9 @@ block body
|
||||
a.option(href="/user/manage/email")
|
||||
i.fa.fa-fw.fa-envelope
|
||||
|Change Email Address
|
||||
.clients
|
||||
h2 OAuth2 Authorized Clients
|
||||
.cl#clientlist
|
||||
span.load
|
||||
i.fa.fa-spin.fa-spinner.fa-2x
|
||||
span Loading list
|
@ -1,4 +1,4 @@
|
||||
extends layout.pug
|
||||
extends ../layout.pug
|
||||
block title
|
||||
|Icy Network - Log In - Verification Required
|
||||
|
@ -1,4 +1,4 @@
|
||||
extends layout.pug
|
||||
extends ../layout.pug
|
||||
block title
|
||||
|Icy Network - Activate Authenticator
|
||||
|
Reference in New Issue
Block a user