mirror of
https://github.com/umami-software/umami.git
synced 2026-02-20 20:45:39 +01:00
Merge branch 'dev' into cockroach-db
This commit is contained in:
commit
f40a20f7e3
63 changed files with 2194 additions and 1132 deletions
43
lib/auth.js
43
lib/auth.js
|
|
@ -1,44 +1,47 @@
|
|||
import { parseSecureToken, parseToken } from 'next-basics';
|
||||
import { getWebsite } from 'queries';
|
||||
import { SHARE_TOKEN_HEADER } from 'lib/constants';
|
||||
import { getAccount, getWebsite } from 'queries';
|
||||
import debug from 'debug';
|
||||
import { SHARE_TOKEN_HEADER, TYPE_ACCOUNT, TYPE_WEBSITE } from 'lib/constants';
|
||||
import { secret } from 'lib/crypto';
|
||||
|
||||
export function getAuthToken(req) {
|
||||
const log = debug('umami:auth');
|
||||
|
||||
export function parseAuthToken(req) {
|
||||
try {
|
||||
const token = req.headers.authorization;
|
||||
|
||||
return parseSecureToken(token.split(' ')[1], secret());
|
||||
} catch {
|
||||
} catch (e) {
|
||||
log(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function getShareToken(req) {
|
||||
export function parseShareToken(req) {
|
||||
try {
|
||||
return parseSecureToken(req.headers[SHARE_TOKEN_HEADER], secret());
|
||||
} catch {
|
||||
return parseToken(req.headers[SHARE_TOKEN_HEADER], secret());
|
||||
} catch (e) {
|
||||
log(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function isValidToken(token, validation) {
|
||||
try {
|
||||
const result = parseToken(token, secret());
|
||||
|
||||
if (typeof validation === 'object') {
|
||||
return !Object.keys(validation).find(key => result[key] !== validation[key]);
|
||||
return !Object.keys(validation).find(key => token[key] !== validation[key]);
|
||||
} else if (typeof validation === 'function') {
|
||||
return validation(result);
|
||||
return validation(token);
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function allowQuery(req) {
|
||||
const { id: websiteId } = req.query;
|
||||
export async function allowQuery(req, type) {
|
||||
const { id } = req.query;
|
||||
|
||||
const { userId, isAdmin, shareToken } = req.auth ?? {};
|
||||
|
||||
|
|
@ -47,13 +50,19 @@ export async function allowQuery(req) {
|
|||
}
|
||||
|
||||
if (shareToken) {
|
||||
return isValidToken(shareToken, { websiteUuid: websiteId });
|
||||
return isValidToken(shareToken, { id });
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
const website = await getWebsite({ websiteUuid: websiteId });
|
||||
if (type === TYPE_WEBSITE) {
|
||||
const website = await getWebsite({ websiteUuid: id });
|
||||
|
||||
return website && website.userId === userId;
|
||||
return website && website.userId === userId;
|
||||
} else if (type === TYPE_ACCOUNT) {
|
||||
const account = await getAccount({ accountUuid: id });
|
||||
|
||||
return account && account.accountUuid === id;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ export const DEFAULT_WEBSITE_LIMIT = 10;
|
|||
export const REALTIME_RANGE = 30;
|
||||
export const REALTIME_INTERVAL = 3000;
|
||||
|
||||
export const TYPE_WEBSITE = 'website';
|
||||
export const TYPE_ACCOUNT = 'account';
|
||||
|
||||
export const THEME_COLORS = {
|
||||
light: {
|
||||
primary: '#2680eb',
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ export function secret() {
|
|||
export function salt() {
|
||||
const ROTATING_SALT = hash(startOfMonth(new Date()).toUTCString());
|
||||
|
||||
return hash([secret(), ROTATING_SALT]);
|
||||
return hash(secret(), ROTATING_SALT);
|
||||
}
|
||||
|
||||
export function uuid(...args) {
|
||||
if (!args.length) return v4();
|
||||
|
||||
return v5(hash([...args, salt()]), v5.DNS);
|
||||
return v5(hash(...args, salt()), v5.DNS);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { createMiddleware, unauthorized, badRequest, serverError } from 'next-basics';
|
||||
import cors from 'cors';
|
||||
import { getSession } from './session';
|
||||
import { getAuthToken, getShareToken } from './auth';
|
||||
import { parseAuthToken, parseShareToken } from './auth';
|
||||
|
||||
export const useCors = createMiddleware(cors());
|
||||
|
||||
|
|
@ -26,10 +26,10 @@ export const useSession = createMiddleware(async (req, res, next) => {
|
|||
});
|
||||
|
||||
export const useAuth = createMiddleware(async (req, res, next) => {
|
||||
const token = await getAuthToken(req);
|
||||
const shareToken = await getShareToken(req);
|
||||
const token = await parseAuthToken(req);
|
||||
const shareToken = await parseShareToken(req);
|
||||
|
||||
if (!token) {
|
||||
if (!token && !shareToken) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { secret, uuid } from 'lib/crypto';
|
|||
import redis, { DELETED } from 'lib/redis';
|
||||
import clickhouse from 'lib/clickhouse';
|
||||
import { getClientInfo, getJsonBody } from 'lib/request';
|
||||
import { createSession, getSessionByUuid, getWebsiteByUuid } from 'queries';
|
||||
import { createSession, getSessionByUuid, getWebsite } from 'queries';
|
||||
|
||||
export async function getSession(req) {
|
||||
const { payload } = getJsonBody(req);
|
||||
|
|
@ -38,7 +38,7 @@ export async function getSession(req) {
|
|||
|
||||
// Check database if does not exists in Redis
|
||||
if (!websiteId) {
|
||||
const website = await getWebsiteByUuid(websiteUuid);
|
||||
const website = await getWebsite({ websiteUuid });
|
||||
websiteId = website ? website.id : null;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue