keycloak-mock
Advanced tools
Comparing version 0.0.7 to 0.0.8
@@ -1,3 +0,10 @@ | ||
export { Mock, MockOptions, activateMock, deactivateMock, getMock, getMockInstance } from "./mock"; | ||
export { | ||
Mock, | ||
MockOptions, | ||
activateMock, | ||
deactivateMock, | ||
getMock, | ||
getMockInstance, | ||
} from "./mock"; | ||
export { | ||
MockInstance, | ||
@@ -8,3 +15,10 @@ MockInstanceParams, | ||
} from "./instance"; | ||
export { default as MockDatabase, MockUserProfile, CreateMockUserProfileOptions } from "./database"; | ||
export { default as createBearerToken, CreateTokenOptions } from "./createBearerToken"; | ||
export { | ||
default as MockDatabase, | ||
MockUserProfile, | ||
CreateMockUserProfileOptions, | ||
} from "./database"; | ||
export { | ||
default as createBearerToken, | ||
CreateTokenOptions, | ||
} from "./createBearerToken"; |
import { JWK } from "node-jose"; | ||
import createBearerToken from "./createBearerToken"; | ||
import MockDatabase, { MockUserProfile, CreateMockUserProfileOptions } from "./database"; | ||
import MockDatabase, { | ||
MockUserProfile, | ||
CreateMockUserProfileOptions, | ||
} from "./database"; | ||
@@ -36,2 +39,6 @@ export interface CreateMockInstanceOptions { | ||
createURL(path: string): string { | ||
return `${this.params.authServerURL}${path}`; | ||
} | ||
createBearerToken(sub: string, expiresIn: number = 3600): string { | ||
@@ -54,3 +61,5 @@ const user = this.database.findUserByID(sub); | ||
const createMockInstance = async (options: CreateMockInstanceOptions): Promise<MockInstance> => { | ||
const createMockInstance = async ( | ||
options: CreateMockInstanceOptions | ||
): Promise<MockInstance> => { | ||
const store = JWK.createKeyStore(); | ||
@@ -57,0 +66,0 @@ const defaultKey = await store.generate("RSA", 2048, { use: "sig" }); |
import nock, { Scope } from "nock"; | ||
import { ViewFn } from "./types"; | ||
import { MockInstance } from "./instance"; | ||
import { decodeTokenAndAttachUser } from "./middlewares"; | ||
import { listCertificates, getUser, getUserInfo } from "./views"; | ||
import { ViewFn, listCertificates, getUser, getUserInfo } from "./views"; | ||
let __activeMocks__: Map<string, Mock> = new Map<string, Mock>(); | ||
@@ -31,3 +32,5 @@ | ||
.get(`/realms/${realm}/protocol/openid-connect/certs`) | ||
.reply(function() { | ||
.reply(async function() { | ||
await decodeTokenAndAttachUser(instance, this.req); | ||
if (options && options.listCertificatesView) { | ||
@@ -40,3 +43,5 @@ return options.listCertificatesView(instance, this.req); | ||
.get(new RegExp(`/admin/realms/${realm}/users/(.+)`)) | ||
.reply(function() { | ||
.reply(async function() { | ||
await decodeTokenAndAttachUser(instance, this.req); | ||
if (options && options.getUserView) { | ||
@@ -49,3 +54,5 @@ return options.getUserView(instance, this.req); | ||
.get(`/realms/${realm}/protocol/openid-connect/userinfo`) | ||
.reply(function() { | ||
.reply(async function() { | ||
await decodeTokenAndAttachUser(instance, this.req); | ||
if (options && options.getUserInfoView) { | ||
@@ -52,0 +59,0 @@ return options.getUserInfoView(instance, this.req); |
@@ -1,48 +0,9 @@ | ||
import jwt from "jsonwebtoken"; | ||
import { JWK } from "node-jose"; | ||
import head from "lodash/head"; | ||
import last from "lodash/last"; | ||
import { ViewFn } from "../types"; | ||
import { MockInstance } from "../instance"; | ||
import { ViewFn } from "./types"; | ||
const getUser: ViewFn = (instance, request) => { | ||
const { user } = request; | ||
const getUser: ViewFn = async (instance, request) => { | ||
const token = (head(request.headers.authorization) || "").split(" ")[1]; | ||
if (!token) { | ||
return [401, "Authorization required"]; | ||
} | ||
// @ts-ignore | ||
let decodedToken = null; | ||
try { | ||
decodedToken = jwt.decode(token, { complete: true }); | ||
} catch (error) { | ||
return [403, "Access denied - cannot decode"]; | ||
} | ||
// @ts-ignore | ||
const rawKey = instance.store.get(decodedToken.header.kid); | ||
if (!rawKey) { | ||
return [403, "Access denied - non-existent key"]; | ||
} | ||
const key = await JWK.asKey(rawKey); | ||
try { | ||
jwt.verify(token, key.toPEM(false), { algorithms: ["RS256"] }); | ||
} catch (error) { | ||
return [403, "Access denied - invalid signature"]; | ||
} | ||
const requestSub = last(request.path.split("/")); | ||
// @ts-ignore | ||
if (requestSub !== decodedToken.payload.sub) { | ||
return [403, "Access denied - not you"]; | ||
} | ||
// @ts-ignore | ||
const user = instance.database.findUserByID(decodedToken.payload.sub); | ||
if (!user) { | ||
return [403, "Access denied - non-existent user"]; | ||
return [403, "Access denied"]; | ||
} | ||
@@ -49,0 +10,0 @@ |
@@ -1,41 +0,9 @@ | ||
import jwt from "jsonwebtoken"; | ||
import { JWK } from "node-jose"; | ||
import head from "lodash/head"; | ||
import { ViewFn } from "../types"; | ||
import { MockInstance } from "../instance"; | ||
import { ViewFn } from "./types"; | ||
const getUserInfo: ViewFn = (instance, request) => { | ||
const { user } = request; | ||
const getUserInfo: ViewFn = async (instance, request) => { | ||
const token = (head(request.headers.authorization) || "").split(" ")[1]; | ||
if (!token) { | ||
return [401, "Authorization required"]; | ||
} | ||
// @ts-ignore | ||
let decodedToken = null; | ||
try { | ||
decodedToken = jwt.decode(token, { complete: true }); | ||
} catch (error) { | ||
return [403, "Access denied - cannot decode"]; | ||
} | ||
// @ts-ignore | ||
const rawKey = instance.store.get(decodedToken.header.kid); | ||
if (!rawKey) { | ||
return [403, "Access denied - non-existent key"]; | ||
} | ||
const key = await JWK.asKey(rawKey); | ||
try { | ||
jwt.verify(token, key.toPEM(false), { algorithms: ["RS256"] }); | ||
} catch (error) { | ||
return [403, "Access denied - invalid signature"]; | ||
} | ||
// @ts-ignore | ||
const user = instance.database.findUserByID(decodedToken.payload.sub); | ||
if (!user) { | ||
return [403, "Access denied - non-existent user"]; | ||
return [403, "Access denied"]; | ||
} | ||
@@ -42,0 +10,0 @@ |
export { default as listCertificates } from "./listCertificates"; | ||
export { default as getUser } from "./getUser"; | ||
export { default as getUserInfo } from "./getUserInfo"; | ||
export { ViewFn } from "./types"; |
@@ -1,9 +0,8 @@ | ||
import { ViewFn } from "./types"; | ||
import { ViewFn } from "../types"; | ||
import { MockInstance } from "../instance"; | ||
const listCertificates: ViewFn = (instance, request) => { | ||
return Promise.resolve([200, instance.store.toJSON(false)]); | ||
return [200, instance.store.toJSON(false)]; | ||
}; | ||
export default listCertificates; |
{ | ||
"name": "keycloak-mock", | ||
"version": "0.0.7", | ||
"version": "0.0.8", | ||
"description": "Keycloak server mock for Node.js", | ||
@@ -9,4 +9,8 @@ "main": "dist/index.js", | ||
"license": "MIT", | ||
"engines": { | ||
"node": ">=8.0.0" | ||
}, | ||
"scripts": { | ||
"build": "tsc", | ||
"test": "jest -i tests/", | ||
"format": "prettier --write '**/*.ts'", | ||
@@ -17,6 +21,10 @@ "format:verify": "prettier --check '**/*.ts'", | ||
"devDependencies": { | ||
"@types/jest": "^25.1.3", | ||
"@types/jsonwebtoken": "^8.3.7", | ||
"@types/node-jose": "^1.1.2", | ||
"@types/uuid": "^7.0.0", | ||
"axios": "^0.19.2", | ||
"jest": "^24.0.0", | ||
"prettier": "^1.19.1", | ||
"ts-jest": "^24.3.0", | ||
"typescript": "^3.8.2" | ||
@@ -30,4 +38,4 @@ }, | ||
"node-jose": "^1.1.3", | ||
"uuid": "^7.0.1" | ||
"uuid": "^7.0.2" | ||
} | ||
} |
@@ -12,5 +12,8 @@ # Keycloak Mock | ||
Tested with Node.js 8.x, 10.x, 12.x, 13.x | ||
### What works | ||
* `/auth/[realm]/protocol/openid-connect/certs` | ||
* `/auth/[realm]/protocol/openid-connect/userinfo` | ||
* `/[realm]/protocol/openid-connect/certs` | ||
* `/[realm]/protocol/openid-connect/userinfo` | ||
* `/admin/realms/[realm]/users/[userid]` | ||
@@ -59,7 +62,12 @@ ## Usage | ||
listCertificatesView: (instance, request) => { | ||
return Promise.resolve([500, ""]); | ||
return [500, ""]; | ||
}, | ||
getUser: (instance, request) => { | ||
// might be null if not authorized | ||
console.log(request.user); | ||
return [500, ""]; | ||
}, | ||
getUserInfoView: (instance, request) => { | ||
return Promise.resolve([500, ""]); | ||
return [500, ""]; | ||
}, | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
87612
73
1897
72
9
Updateduuid@^7.0.2