mirror of
https://github.com/wassname/talk.git
synced 2026-07-30 12:40:41 +08:00
Merge branch 'master' into safari-cookie-time
This commit is contained in:
+35
@@ -280,6 +280,41 @@ send data to the client. If the type in question contains args, clients may subs
|
||||
|
||||
For more information, see the [Apollo Docs](https://github.com/apollographql/graphql-subscriptions).
|
||||
|
||||
#### Field: `tokenUserNotFound`
|
||||
|
||||
```js
|
||||
tokenUserNotFound: async ({jwt, token}) => {
|
||||
let profile = await someExternalService(token);
|
||||
if (!profile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let user = await UserModel.findOneAndUpdate({
|
||||
id: profile.id
|
||||
}, {
|
||||
id: profile.id,
|
||||
username: profile.username,
|
||||
lowercaseUsername: profile.username.toLowerCase(),
|
||||
roles: [],
|
||||
profiles: []
|
||||
}, {
|
||||
setDefaultsOnInsert: true,
|
||||
new: true,
|
||||
upsert: true
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
```
|
||||
|
||||
The `tokenUserNotFound` hook allows auth integrations to hook into the event
|
||||
when a valid token is provided but a user can't be found in the database that
|
||||
matches the provided id.
|
||||
|
||||
The function is async, and should return the user object that was created in the
|
||||
database, or null if the user wasn't found. The `jwt` paramenter of the object
|
||||
is the unpacked token, while `token` is the original jwt token string.
|
||||
|
||||
#### Field: `router`
|
||||
|
||||
```js
|
||||
|
||||
@@ -41,10 +41,10 @@ export default class Embed extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<div className="commentStream">
|
||||
<TabBar onChange={this.changeTab} activeTab={activeTab}>
|
||||
<Tab><Count count={totalCommentCount} /></Tab>
|
||||
<Tab>{t('framework.my_profile')}</Tab>
|
||||
<Tab restricted={!can(user, 'UPDATE_CONFIG')}>{t('framework.configure_stream')}</Tab>
|
||||
<TabBar onChange={this.changeTab} activeTab={activeTab} className='talk-stream-tabbar'>
|
||||
<Tab className={'talk-stream-comment-count-tab'}><Count count={totalCommentCount}/></Tab>
|
||||
<Tab className={'talk-stream-profile-tab'}>{t('framework.my_profile')}</Tab>
|
||||
<Tab className={'talk-stream-configuration-tab'} restricted={!can(user, 'UPDATE_CONFIG')}>{t('framework.configure_stream')}</Tab>
|
||||
</TabBar>
|
||||
{commentId &&
|
||||
<Button
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
import styles from './Tab.css';
|
||||
|
||||
export default ({children, tabId, active, onTabClick, cStyle = 'base'}) => (
|
||||
export default ({children, tabId, active, onTabClick, cStyle = 'base', ...props}) => (
|
||||
<li
|
||||
key={tabId}
|
||||
className={active ? styles[`${cStyle}--active`] : ''}
|
||||
className={`${active ? styles[`${cStyle}--active`] : ''} tab ${props.className}`}
|
||||
onClick={() => onTabClick(tabId)}
|
||||
>
|
||||
{children}
|
||||
</li>
|
||||
);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class TabBar extends React.Component {
|
||||
const {children, activeTab, cStyle = 'base'} = this.props;
|
||||
return (
|
||||
<div>
|
||||
<ul className={`${styles.base} ${cStyle ? styles[cStyle] : ''}`}>
|
||||
<ul className={`${styles.base} ${cStyle ? styles[cStyle] : ''} ${this.props.className}`}>
|
||||
{React.Children.toArray(children)
|
||||
.filter((child) => !child.props.restricted)
|
||||
.map((child, tabId) =>
|
||||
|
||||
+19
-3
@@ -173,6 +173,7 @@ const CheckBlacklisted = (jwt) => new Promise((resolve, reject) => {
|
||||
});
|
||||
});
|
||||
|
||||
const jwt = require('jsonwebtoken');
|
||||
const JwtStrategy = require('passport-jwt').Strategy;
|
||||
const ExtractJwt = require('passport-jwt').ExtractJwt;
|
||||
|
||||
@@ -186,6 +187,19 @@ let cookieExtractor = function(req) {
|
||||
return token;
|
||||
};
|
||||
|
||||
// Override the JwtVerifier method on the JwtStrategy so we can pack the
|
||||
// original token into the payload.
|
||||
JwtStrategy.JwtVerifier = (token, secretOrKey, options, callback) => {
|
||||
return jwt.verify(token, secretOrKey, options, (err, jwt) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// Attach the original token onto the payload.
|
||||
return callback(false, {token, jwt});
|
||||
});
|
||||
};
|
||||
|
||||
// Extract the JWT from the 'Authorization' header with the 'Bearer' scheme.
|
||||
passport.use(new JwtStrategy({
|
||||
|
||||
@@ -208,10 +222,10 @@ passport.use(new JwtStrategy({
|
||||
// Enable only the HS256 algorithm.
|
||||
algorithms: ['HS256'],
|
||||
|
||||
// Pass the request objecto back to the callback so we can attach the JWT to
|
||||
// Pass the request object back to the callback so we can attach the JWT to
|
||||
// it.
|
||||
passReqToCallback: true
|
||||
}, async (req, jwt, done) => {
|
||||
}, async (req, {token, jwt}, done) => {
|
||||
|
||||
// Load the user from the environment, because we just got a user from the
|
||||
// header.
|
||||
@@ -220,7 +234,9 @@ passport.use(new JwtStrategy({
|
||||
// Check to see if the token has been revoked
|
||||
await CheckBlacklisted(jwt);
|
||||
|
||||
let user = await UsersService.findById(jwt.sub);
|
||||
// Try to get the user from the database or crack it from the token and
|
||||
// plugin integrations.
|
||||
let user = await UsersService.findOrCreateByIDToken(jwt.sub, {token, jwt});
|
||||
|
||||
// Attach the JWT to the request.
|
||||
req.jwt = jwt;
|
||||
|
||||
@@ -9,6 +9,7 @@ const {
|
||||
JWT_SECRET,
|
||||
ROOT_URL
|
||||
} = require('../config');
|
||||
const debug = require('debug')('talk:services:users');
|
||||
|
||||
const redis = require('./redis');
|
||||
const redisClient = redis.createClient();
|
||||
@@ -526,6 +527,29 @@ module.exports = class UsersService {
|
||||
return UserModel.findOne({id});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} id the id of the current user
|
||||
* @param {Object} token a jwt token used to sign in the user
|
||||
*/
|
||||
static async findOrCreateByIDToken(id, token) {
|
||||
|
||||
// Try to get the user.
|
||||
let user = await UserModel.findOne({
|
||||
id
|
||||
});
|
||||
|
||||
// If the user was not found, try to look it up.
|
||||
if (user === null) {
|
||||
|
||||
// If the user wasn't found, it will return null and the variable will be
|
||||
// unchanged.
|
||||
user = await lookupUserNotFound(token);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds users in an array of ids.
|
||||
* @param {Array} ids array of user identifiers (uuid)
|
||||
@@ -903,3 +927,25 @@ module.exports = class UsersService {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Extract all the tokenUserNotFound plugins so we can integrate with other
|
||||
// providers.
|
||||
const tokenUserNotFoundHooks = require('./plugins')
|
||||
.get('server', 'tokenUserNotFound')
|
||||
.map(({plugin, tokenUserNotFound}) => {
|
||||
debug(`added plugin '${plugin.name}' to tokenUserNotFound hooks`);
|
||||
|
||||
return tokenUserNotFound;
|
||||
});
|
||||
|
||||
// Provide a function that
|
||||
const lookupUserNotFound = async (token) => {
|
||||
for (let hook of tokenUserNotFoundHooks) {
|
||||
let user = await hook(token);
|
||||
if (user !== null && typeof user !== 'undefined') {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user