mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: added login test and fixed super admin bug
This commit is contained in:
45
backend-pg/e2e-test/routes/v1/login.spec.ts
Normal file
45
backend-pg/e2e-test/routes/v1/login.spec.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { testUser } from "@app/db/seeds/1-user";
|
||||
import jsrp from "jsrp";
|
||||
|
||||
describe("Login V1 Router", async () => {
|
||||
// eslint-disable-next-line
|
||||
const client = new jsrp.client();
|
||||
await new Promise((resolve) => {
|
||||
client.init({ username: testUser.email, password: testUser.password }, () => resolve(null));
|
||||
});
|
||||
let clientProof: string;
|
||||
|
||||
test("Login first phase", async () => {
|
||||
const res = await testServer.inject({
|
||||
method: "POST",
|
||||
url: "/api/v3/auth/login1",
|
||||
body: {
|
||||
email: "test@localhost.local",
|
||||
clientPublicKey: client.getPublicKey()
|
||||
}
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
const payload = JSON.parse(res.payload);
|
||||
expect(payload).toHaveProperty("serverPublicKey");
|
||||
expect(payload).toHaveProperty("salt");
|
||||
client.setSalt(payload.salt);
|
||||
client.setServerPublicKey(payload.serverPublicKey);
|
||||
clientProof = client.getProof(); // called M1
|
||||
});
|
||||
|
||||
test("Login second phase", async () => {
|
||||
const res = await testServer.inject({
|
||||
method: "POST",
|
||||
url: "/api/v3/auth/login2",
|
||||
body: {
|
||||
email: testUser.email,
|
||||
clientProof
|
||||
}
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
const payload = JSON.parse(res.payload);
|
||||
expect(payload).toHaveProperty("mfaEnabled");
|
||||
expect(payload).toHaveProperty("token");
|
||||
expect(payload.mfaEnabled).toBeFalsy();
|
||||
});
|
||||
});
|
||||
9
backend-pg/e2e-test/routes/v1/test.spec.ts
Normal file
9
backend-pg/e2e-test/routes/v1/test.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
describe("Status V1 Router", async () => {
|
||||
test("Simple check", async () => {
|
||||
const res = await testServer.inject({
|
||||
method: "GET",
|
||||
url: "/api/status"
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
});
|
||||
});
|
||||
@@ -20,7 +20,8 @@ export default {
|
||||
connection: process.env.DB_CONNECTION_URI,
|
||||
migrations: {
|
||||
directory: path.join(__dirname, "../src/db/migrations"),
|
||||
extension: "ts"
|
||||
extension: "ts",
|
||||
tableName: "infisical_migrations"
|
||||
},
|
||||
seeds: {
|
||||
directory: path.join(__dirname, "../src/db/seeds"),
|
||||
|
||||
27
backend-pg/package-lock.json
generated
27
backend-pg/package-lock.json
generated
@@ -89,6 +89,7 @@
|
||||
"prompt-sync": "^4.2.0",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"tsup": "^8.0.1",
|
||||
"tsx": "^4.4.0",
|
||||
"typescript": "^5.3.2",
|
||||
@@ -8175,6 +8176,18 @@
|
||||
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
|
||||
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonc-parser": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
|
||||
@@ -11986,6 +11999,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tsconfig-paths": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
|
||||
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"json5": "^2.2.2",
|
||||
"minimist": "^1.2.6",
|
||||
"strip-bom": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
"prompt-sync": "^4.2.0",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"tsup": "^8.0.1",
|
||||
"tsx": "^4.4.0",
|
||||
"typescript": "^5.3.2",
|
||||
|
||||
@@ -1,10 +1,78 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import jsrp from "jsrp";
|
||||
import nacl from "tweetnacl";
|
||||
import { encodeBase64 } from "tweetnacl-util";
|
||||
import argon2, { argon2id } from "argon2";
|
||||
import { AuthMethod } from "../../services/auth/auth-type";
|
||||
import { TableName } from "../schemas";
|
||||
import crypto from "node:crypto";
|
||||
import { encryptSymmetric } from "@app/lib/crypto";
|
||||
|
||||
export const testUser = {
|
||||
email: "test@localhost.local"
|
||||
email: "test@localhost.local",
|
||||
password: process.env.TEST_USER_PASSWORD || "testInfisical@1"
|
||||
};
|
||||
|
||||
export const generateUserSrpKeys = async (password: string) => {
|
||||
const pair = nacl.box.keyPair();
|
||||
const secretKeyUint8Array = pair.secretKey;
|
||||
const publicKeyUint8Array = pair.publicKey;
|
||||
const privateKey = encodeBase64(secretKeyUint8Array);
|
||||
const publicKey = encodeBase64(publicKeyUint8Array);
|
||||
|
||||
// eslint-disable-next-line
|
||||
const client = new jsrp.client();
|
||||
await new Promise((resolve) => {
|
||||
client.init({ username: testUser.email, password: testUser.password }, () => resolve(null));
|
||||
});
|
||||
const { salt, verifier } = await new Promise<{ salt: string; verifier: string }>(
|
||||
(resolve, reject) => {
|
||||
client.createVerifier((err, res) => {
|
||||
if (err) return reject(err);
|
||||
return resolve(res);
|
||||
});
|
||||
}
|
||||
);
|
||||
const derivedKey = await argon2.hash(password, {
|
||||
salt: Buffer.from(salt),
|
||||
memoryCost: 65536,
|
||||
timeCost: 3,
|
||||
parallelism: 1,
|
||||
hashLength: 32,
|
||||
type: argon2id,
|
||||
raw: true
|
||||
});
|
||||
if (!derivedKey) throw new Error("Failed to derive key from password");
|
||||
|
||||
const key = crypto.randomBytes(32);
|
||||
|
||||
// create encrypted private key by encrypting the private
|
||||
// key with the symmetric key [key]
|
||||
const {
|
||||
ciphertext: encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag
|
||||
} = encryptSymmetric(privateKey, key.toString("base64"));
|
||||
|
||||
// create the protected key by encrypting the symmetric key
|
||||
// [key] with the derived key
|
||||
const {
|
||||
ciphertext: protectedKey,
|
||||
iv: protectedKeyIV,
|
||||
tag: protectedKeyTag
|
||||
} = encryptSymmetric(key.toString("hex"), derivedKey.toString("base64"));
|
||||
|
||||
return {
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier
|
||||
};
|
||||
};
|
||||
|
||||
export async function seed(knex: Knex): Promise<void> {
|
||||
@@ -17,7 +85,7 @@ export async function seed(knex: Knex): Promise<void> {
|
||||
const [user] = await knex(TableName.Users)
|
||||
.insert([
|
||||
{
|
||||
email: "test@localhost.local",
|
||||
email: testUser.email,
|
||||
superAdmin: true,
|
||||
firstName: "test",
|
||||
lastName: "",
|
||||
@@ -30,21 +98,20 @@ export async function seed(knex: Knex): Promise<void> {
|
||||
])
|
||||
.returning("*");
|
||||
|
||||
const encKeys = await generateUserSrpKeys(testUser.password);
|
||||
// password: testInfisical@1
|
||||
await knex(TableName.UserEncryptionKey).insert([
|
||||
{
|
||||
encryptionVersion: 2,
|
||||
protectedKey:
|
||||
"Ng0qHLdRdoLR4lHS0xsJVYLR6Y9F44MjkC9dKz1AcGMcoN1VuXjCMySshLPAj2Fboyz9Jo7Qc72YLTaHUGaubA==",
|
||||
protectedKeyIV: "ou9NrOxwQTYJUdIMrQDuPQ==",
|
||||
protectedKeyTag: "BZQMY7mE14maBKzPZpCgtQ==",
|
||||
publicKey: "rH+riApRZX6HkHMBOhyDhnUBzWWMOx/EBx4gnHYRUTs=",
|
||||
encryptedPrivateKey: "aNnCmeG6sWh7qF40QsFQx9wfgpRTMLtnrtZ+DgkvNXOPYaPW1n2YnH+g20k=",
|
||||
iv: "Q4O3elA0iwUvsgSOW0rIkA==",
|
||||
tag: "RCuZe9paDKS71hluuX7qbw==",
|
||||
salt: "faac495e264903a7cd42bd75298ddecbf27824f11a744a26788e20b5421ec7e0",
|
||||
verifier:
|
||||
"e1f9aff68b4e2c55f21652527bcb070dd171ea2b8de8411890ad13dd7e2da965f29cd5f43a7786690764c3f6233c32c5f98c8da89cd2869bdf940121fff3c25888caa05d8d76c8bb88ddb9c706f2a8f336778c24f64d70f498bd154ac4bf9d79241ed20b343c1fa6d8061cc834401ef89df615bb6e66388dafafad57ed5b334899846d2f031d6134a6681b0cf9ca5ccd13aac53992f564e5550b26222b41ef9c3b6c4e3b8c0ee654762c8feb8a450d4eb9154f7d21b6b409d1027dc298b3240fb7795bae96415dccac689ba705efb82bae469107832bffc703abf6ec532930241061bb88bca3df2ee187ebe06a8f79ccfc851a5f90a83d5703b9ebfc0d08b9ad626870cad09b2ba7ba82c5297f242f5a012bb92c137190fd0168f83f24b62a01431a796b2aba2e133a71106e4271a07c5bdbeeddce5680f785371f5388941e736140b275d6df79cf9f8ce99b6575f7ea4739e7b6fba2f46e53e4bf8e01ca16aecb1138c218fb29b33473515008917f64067ae4104f9eac9abca193ee7596e70de1277a1c789af883d297eb6e72d106ec9899e32d716843ecb8295d1ec54d67b6a754ff5db75ae8233d6472106a7eefb0f9d0b62fe1e2278ec8c4e52e9c65bf2ec2c4e9a20f63c839ce36e67bb5519ef2b425399bad03b47aa0cb5b369444bb13e20d15548da348d6ab852600c35af36f3df8dd1bfc7fa2c00ebf68bc3033da00",
|
||||
protectedKey: encKeys.protectedKey,
|
||||
protectedKeyIV: encKeys.protectedKeyIV,
|
||||
protectedKeyTag: encKeys.protectedKeyTag,
|
||||
publicKey: encKeys.publicKey,
|
||||
encryptedPrivateKey: encKeys.encryptedPrivateKey,
|
||||
iv: encKeys.encryptedPrivateKeyIV,
|
||||
tag: encKeys.encryptedPrivateKeyTag,
|
||||
salt: encKeys.salt,
|
||||
verifier: encKeys.verifier,
|
||||
userId: user.id
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -472,7 +472,7 @@ export const registerRoutes = async (
|
||||
await server.register(injectAuditLogInfo);
|
||||
|
||||
server.route({
|
||||
url: "/status",
|
||||
url: "/api/status",
|
||||
method: "GET",
|
||||
schema: {
|
||||
response: {
|
||||
|
||||
@@ -37,9 +37,10 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
preHandler: (req) => {
|
||||
onRequest: (req, _, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.API_KEY])(req);
|
||||
verifySuperAdmin(req);
|
||||
done();
|
||||
},
|
||||
handler: async (req) => {
|
||||
const config = await server.services.superAdmin.updateServerCfg(req.body);
|
||||
|
||||
@@ -24,7 +24,7 @@ export const superAdminServiceFactory = ({
|
||||
const initServerCfg = async () => {
|
||||
serverCfg = await serverCfgDal.findOne({});
|
||||
if (!serverCfg) {
|
||||
const newCfg = await serverCfgDal.create({ initialized: true, allowSignUp: true });
|
||||
const newCfg = await serverCfgDal.create({ initialized: false, allowSignUp: true });
|
||||
serverCfg = newCfg;
|
||||
return newCfg;
|
||||
}
|
||||
@@ -39,6 +39,7 @@ export const superAdminServiceFactory = ({
|
||||
|
||||
const updateServerCfg = async (data: TSuperAdminUpdate) => {
|
||||
const cfg = await serverCfgDal.updateById(serverCfg.id, data);
|
||||
serverCfg = cfg;
|
||||
return cfg;
|
||||
};
|
||||
|
||||
@@ -59,7 +60,7 @@ export const superAdminServiceFactory = ({
|
||||
userAgent
|
||||
}: TAdminSignUpDTO) => {
|
||||
const existingUser = await userDal.findOne({ email });
|
||||
if (!existingUser)
|
||||
if (existingUser)
|
||||
throw new BadRequestError({ name: "Admin sign up", message: "User already exist" });
|
||||
|
||||
const userInfo = await userDal.transaction(async (tx) => {
|
||||
@@ -68,7 +69,8 @@ export const superAdminServiceFactory = ({
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
superAdmin: true
|
||||
superAdmin: true,
|
||||
isAccepted: true
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
{
|
||||
"ts-node": {
|
||||
// Do not forget to `npm i -D tsconfig-paths`
|
||||
"require": ["tsconfig-paths/register"]
|
||||
},
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "commonjs",
|
||||
"allowJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"types": [
|
||||
"vitest/globals"
|
||||
],
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./src/@types",
|
||||
"./node_modules"
|
||||
],
|
||||
"types": ["vitest/globals"],
|
||||
"typeRoots": ["./node_modules/@types", "./src/@types", "./node_modules"],
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"strict": true,
|
||||
"lib": [
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["esnext"],
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
@@ -26,23 +22,11 @@
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@app/*": [
|
||||
"./src/*"
|
||||
],
|
||||
"@lib/*": [
|
||||
"./src/lib/*"
|
||||
],
|
||||
"@server/*": [
|
||||
"./src/server/*"
|
||||
]
|
||||
"@app/*": ["./src/*"],
|
||||
"@lib/*": ["./src/lib/*"],
|
||||
"@server/*": ["./src/server/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"scripts/**/*",
|
||||
"e2e-test/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
"include": ["src/**/*", "scripts/**/*", "e2e-test/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,13 @@ export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: "./e2e-test/vitest-environment-knex.ts",
|
||||
include: ["./e2e-test/**/*.spec.ts"]
|
||||
include: ["./e2e-test/**/*.spec.ts"],
|
||||
poolOptions: {
|
||||
threads: {
|
||||
singleThread: true,
|
||||
useAtomics: true
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [tsconfigPaths()] // only if you are using custom tsconfig paths,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user