Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use SQLite as backing data store #25

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .env
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# See: https://www.prisma.io/docs/concepts/components/prisma-client/debugging
# DEBUG="*"
# DATABASE_URL=postgresql://postgres:example@localhost:5432/directory?schema=public
# DATABASE_URL=mysql://root:example@localhost:3306/directory?connection_limit=40&pool_timeout=30
# DATABASE_URL=mysql://user1:asdf1@localhost/directory1
DATABASE_URL=mysql://root:example@localhost/directory
# DATABASE_URL=mysql://root:example@localhost/directory
DATABASE_URL=file:/home/jonathan/repos/directory/dev.db?connection_limit=1
MEERKAT_WEB_ADMIN_PORT=18080
MEERKAT_WEB_ADMIN_AUTH_USERNAME=asdf
MEERKAT_WEB_ADMIN_AUTH_PASSWORD=asdf
Expand Down Expand Up @@ -69,8 +72,6 @@ NODE_ENV=development
LC_MESSAGES=en_US.utf8
LC_ALL=en_US.utf8
LANG=en_US.utf8
# See: https://www.prisma.io/docs/concepts/components/prisma-client/debugging
# DEBUG=*
MEERKAT_TEST_TELEMETRY=1 # Undocumented.
MEERKAT_CHAINING_TLS_OPTIONAL=1
MEERKAT_OB_AUTO_ACCEPT=0
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

# Ignore SQLite databases
*.db
demo/

*.snap
Expand Down
12 changes: 12 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@
"${workspaceFolder}/dist/apps/meerkat/**/*.js"
]
},
{
"type": "pwa-node",
"request": "attach",
"port": 9229,
"name": "Attach to Meerkat (Port 9229)",
"skipFiles": [
"<node_internals>/**"
],
"outFiles": [
"${workspaceFolder}/dist/apps/meerkat/**/*.js"
]
},
{
"type": "node",
"request": "attach",
Expand Down
14 changes: 14 additions & 0 deletions apps/meerkat-docs/docs/env.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,20 @@ credentials, etc. are susceptible to inspection by intermediaries, which is a
security problem. These operations may not be susceptible to tampering (other
than by omission) if cryptographic signing is used.

## MEERKAT_DB_CACHE_SIZE

The size in kilobytes of the SQLite cache. SQLite defaults to 2000KB of cache
(2 megabytes), but Meerkat DSA defaults to a 16 megabytes. You should increase
this amount if you have the memory to accomodate more.

## MEERKAT_DB_MMAP_SIZE

The maximum number of bytes of the underlying SQLite database file that will be
accessed using memory-mapped I/O. If set to 0, memory-mapping will be disabled.
The default value is 128 megabytes. If you have more memory available, you
should probably bump this up, but it should not matter unless you're DSA is
serving a lot of concurrent users.

## MEERKAT_DEFAULT_ENTRY_TTL

The default value of the `entryTtl` operational attribute, if an entry
Expand Down
10 changes: 8 additions & 2 deletions apps/meerkat/src/app/admin/home.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { getServerStatistics } from "../telemetry/getServerStatistics";
import sleep from "../utils/sleep";
import stringifyDN from "../x500/stringifyDN";
import { rdnFromJson } from "../x500/rdnFromJson";
import { BERElement } from "asn1-ts";
import { _decode_RelativeDistinguishedName } from "@wildboar/pki-stub/src/lib/modules/PKI-Stub/RelativeDistinguishedName.ta";

const conformancePath = path.join(__dirname, "assets", "static", "conformance.md");
const ROBOTS: string = `User-agent: *\r\nDisallow: /\r\n`;
Expand Down Expand Up @@ -165,8 +167,12 @@ export class HomeController {
if (!ob) {
throw new NotFoundException();
}
const cp_rdn = ((typeof ob.new_context_prefix_rdn === "object") && ob.new_context_prefix_rdn)
? rdnFromJson(ob.new_context_prefix_rdn as Record<string, string>)
const cp_rdn = ob.new_context_prefix_rdn
? (() => {
const el = new BERElement();
el.fromBytes(ob.new_context_prefix_rdn);
return _decode_RelativeDistinguishedName(el);
})()
: undefined;
const cp_superior_dn = Array.isArray(ob.immediate_superior)
? ob.immediate_superior.map(rdnFromJson)
Expand Down
70 changes: 32 additions & 38 deletions apps/meerkat/src/app/authn/attemptPassword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,6 @@ async function attemptPassword (
newFailsEl.value.byteOffset,
newFailsEl.value.byteLength,
),
jer: fails + 1,
},
{
entry_id: vertex.dse.id,
Expand All @@ -497,7 +496,6 @@ async function attemptPassword (
nowElement.value.byteOffset,
nowElement.value.byteLength,
),
jer: nowElement.toJSON() as string,
},
];

Expand All @@ -510,7 +508,6 @@ async function attemptPassword (
constructed: false,
tag_number: ASN1UniversalType.boolean,
content_octets: Buffer.from([ 0xFF ]),
jer: true,
});
new_attrs.push({
entry_id: vertex.dse.id,
Expand All @@ -524,7 +521,6 @@ async function attemptPassword (
nowElement.value.byteOffset,
nowElement.value.byteLength,
),
jer: nowElement.toJSON() as string,
});
}

Expand All @@ -540,9 +536,9 @@ async function attemptPassword (
},
},
}),
ctx.db.attributeValue.createMany({
data: new_attrs,
}),
...new_attrs.map((new_attr) => ctx.db.attributeValue.create({
data: new_attr,
})),
];
await ctx.db.$transaction(dbPromises);
return {
Expand Down Expand Up @@ -627,37 +623,35 @@ async function attemptPassword (
},
},
}),
ctx.db.attributeValue.createMany({
data: [
{
entry_id: vertex.dse.id,
type_oid: pwdFails["&id"].toBytes(),
operational: true,
tag_class: ASN1TagClass.universal,
constructed: false,
tag_number: ASN1UniversalType.integer,
content_octets: Buffer.from(
zeroFailsEl.value.buffer,
zeroFailsEl.value.byteOffset,
zeroFailsEl.value.byteLength,
),
jer: 0,
},
{
entry_id: vertex.dse.id,
type_oid: pwdLastSuccess["&id"].toBytes(),
operational: true,
tag_class: ASN1TagClass.universal,
constructed: false,
tag_number: ASN1UniversalType.generalizedTime,
content_octets: Buffer.from(
nowElement.value.buffer,
nowElement.value.byteOffset,
nowElement.value.byteLength,
),
jer: now.toISOString(),
},
],
ctx.db.attributeValue.create({
data: {
entry_id: vertex.dse.id,
type_oid: pwdFails["&id"].toBytes(),
operational: true,
tag_class: ASN1TagClass.universal,
constructed: false,
tag_number: ASN1UniversalType.integer,
content_octets: Buffer.from(
zeroFailsEl.value.buffer,
zeroFailsEl.value.byteOffset,
zeroFailsEl.value.byteLength,
),
},
}),
ctx.db.attributeValue.create({
data: {
entry_id: vertex.dse.id,
type_oid: pwdLastSuccess["&id"].toBytes(),
operational: true,
tag_class: ASN1TagClass.universal,
constructed: false,
tag_number: ASN1UniversalType.generalizedTime,
content_octets: Buffer.from(
nowElement.value.buffer,
nowElement.value.byteOffset,
nowElement.value.byteLength,
),
},
}),
]);

Expand Down
13 changes: 12 additions & 1 deletion apps/meerkat/src/app/ctx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,14 @@ const bindOverrides: SigningInfo["bindOverrides"] = {
};

const config: Configuration = {
db: {
mmapSize: process.env.MEERKAT_DB_MMAP_SIZE
? Number.parseInt(process.env.MEERKAT_DB_MMAP_SIZE)
: 128_000_000, // 128MB
cacheSize: process.env.MEERKAT_DB_CACHE_SIZE
? Number.parseInt(process.env.MEERKAT_DB_CACHE_SIZE)
: 16_000, // 16MB
},
vendorName: process.env.MEERKAT_VENDOR_NAME?.length
? process.env.MEERKAT_VENDOR_NAME
: undefined,
Expand Down Expand Up @@ -1085,7 +1093,10 @@ const ctx: MeerkatContext = {
app: "meerkat",
},
}),
db: new PrismaClient(),
db: new PrismaClient({
// log: ["query"],
// log: ['query', 'info', 'warn', 'error'],
}),
telemetry: {
init: async (): Promise<void> => {
try {
Expand Down
74 changes: 35 additions & 39 deletions apps/meerkat/src/app/database/createEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import getStructuralObjectClass from "../x500/getStructuralObjectClass";
import {
entryTtl,
} from "@wildboar/parity-schema/src/lib/modules/RFC2589DynamicDirectory/entryTtl.oa";
import { ASN1Construction, OBJECT_IDENTIFIER } from "asn1-ts";
import { ASN1Construction, DERElement, OBJECT_IDENTIFIER } from "asn1-ts";
import {
Attribute,
} from "@wildboar/x500/src/lib/modules/InformationFramework/Attribute.ta";
Expand Down Expand Up @@ -117,8 +117,8 @@ async function createDse (
immediate_superior_id: superior.dse.id,
materialized_path,
entryUUID: randomUUID(),
creatorsName: [],
modifiersName: [],
creatorsName: DERElement.fromSequence([]).toBytes(),
modifiersName: DERElement.fromSequence([]).toBytes(),
createTimestamp: entryInit.createTimestamp ?? now,
modifyTimestamp: entryInit.modifyTimestamp ?? now,
// This entry is intentionally created as deleted first, in case the transaction fails.
Expand All @@ -139,21 +139,19 @@ async function createDse (
structuralObjectClass: entryInit.structuralObjectClass ?? soc.toString(),
governingStructureRule: entryInit.governingStructureRule,
RDN: {
createMany: {
data: rdn.map((atav, i): Prisma.DistinguishedValueCreateWithoutEntryInput => ({
type_oid: atav.type_.toBytes(),
tag_class: atav.value.tagClass,
constructed: (atav.value.construction === ASN1Construction.constructed),
tag_number: atav.value.tagNumber,
content_octets: Buffer.from(
atav.value.value.buffer,
atav.value.value.byteOffset,
atav.value.value.byteLength,
),
order_index: i,
normalized_str: NORMALIZER_GETTER(atav.type_)?.(ctx, atav.value),
})),
},
create: rdn.map((atav, i): Prisma.DistinguishedValueCreateWithoutEntryInput => ({
type_oid: atav.type_.toBytes(),
tag_class: atav.value.tagClass,
constructed: (atav.value.construction === ASN1Construction.constructed),
tag_number: atav.value.tagNumber,
content_octets: Buffer.from(
atav.value.value.buffer,
atav.value.value.byteOffset,
atav.value.value.byteLength,
),
order_index: i,
normalized_str: NORMALIZER_GETTER(atav.type_)?.(ctx, atav.value),
})),
},
EntryAttributeValuesIncomplete: entryInit.EntryAttributeValuesIncomplete,
subordinate_completeness: entryInit.subordinate_completeness,
Expand Down Expand Up @@ -226,8 +224,8 @@ async function createDse (
? {
attributeCompleteness: entryInit.attribute_completeness ?? false,
attributeValuesIncomplete: new Set(
Array.isArray(entryInit.EntryAttributeValuesIncomplete?.createMany?.data)
? (entryInit.EntryAttributeValuesIncomplete?.createMany?.data.map((x) => x.attribute_type) ?? [])
Array.isArray(entryInit.EntryAttributeValuesIncomplete?.create)
? (entryInit.EntryAttributeValuesIncomplete?.create.map((x) => x.attribute_type) ?? [])
: [],
),
subordinateCompleteness: entryInit.subordinate_completeness ?? false,
Expand Down Expand Up @@ -362,8 +360,8 @@ async function createEntry (
immediate_superior_id: superior.dse.id,
materialized_path,
entryUUID: randomUUID(),
creatorsName: [],
modifiersName: [],
creatorsName: DERElement.fromSequence([]).toBytes(),
modifiersName: DERElement.fromSequence([]).toBytes(),
createTimestamp: entryInit.createTimestamp ?? now,
modifyTimestamp: entryInit.modifyTimestamp ?? now,
// This entry is intentionally created as deleted first, in case the transaction fails.
Expand All @@ -385,21 +383,19 @@ async function createEntry (
?? getStructuralObjectClass(ctx, objectClasses).toString(),
governingStructureRule: entryInit.governingStructureRule,
RDN: {
createMany: {
data: rdn.map((atav, i): Prisma.DistinguishedValueCreateWithoutEntryInput => ({
type_oid: atav.type_.toBytes(),
tag_class: atav.value.tagClass,
constructed: (atav.value.construction === ASN1Construction.constructed),
tag_number: atav.value.tagNumber,
content_octets: Buffer.from(
atav.value.value.buffer,
atav.value.value.byteOffset,
atav.value.value.byteLength,
),
order_index: i,
normalized_str: NORMALIZER_GETTER(atav.type_)?.(ctx, atav.value),
})),
},
create: rdn.map((atav, i): Prisma.DistinguishedValueCreateWithoutEntryInput => ({
type_oid: atav.type_.toBytes(),
tag_class: atav.value.tagClass,
constructed: (atav.value.construction === ASN1Construction.constructed),
tag_number: atav.value.tagNumber,
content_octets: Buffer.from(
atav.value.value.buffer,
atav.value.value.byteOffset,
atav.value.value.byteLength,
),
order_index: i,
normalized_str: NORMALIZER_GETTER(atav.type_)?.(ctx, atav.value),
})),
},
EntryAttributeValuesIncomplete: entryInit.EntryAttributeValuesIncomplete,
subordinate_completeness: entryInit.subordinate_completeness,
Expand Down Expand Up @@ -471,8 +467,8 @@ async function createEntry (
? {
attributeCompleteness: entryInit.attribute_completeness ?? false,
attributeValuesIncomplete: new Set(
Array.isArray(entryInit.EntryAttributeValuesIncomplete?.createMany?.data)
? (entryInit.EntryAttributeValuesIncomplete?.createMany?.data.map((x) => x.attribute_type) ?? [])
Array.isArray(entryInit.EntryAttributeValuesIncomplete?.create)
? (entryInit.EntryAttributeValuesIncomplete?.create.map((x) => x.attribute_type) ?? [])
: [],
),
subordinateCompleteness: entryInit.subordinate_completeness ?? false,
Expand Down
7 changes: 3 additions & 4 deletions apps/meerkat/src/app/database/drivers/aliasedEntryName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
_decode_DistinguishedName,
_encode_DistinguishedName,
} from "@wildboar/x500/src/lib/modules/InformationFramework/DistinguishedName.ta";
import rdnToJson from "../../x500/rdnToJson";
import dnToID from "../../dit/dnToID";

export
Expand Down Expand Up @@ -53,7 +52,7 @@ const addValue: SpecialAttributeDatabaseEditor = async (
pendingUpdates.otherWrites.push(ctx.db.alias.create({
data: {
alias_entry_id: vertex.dse.id,
aliased_entry_name: decodedName.map(rdnToJson),
aliased_entry_name: _encode_DistinguishedName(decodedName, DER).toBytes(),
aliased_entry_id: aliasedEntryId,
},
select: { id: true }, // UNNECESSARY See: https://github.com/prisma/prisma/issues/6252
Expand Down Expand Up @@ -82,7 +81,7 @@ const removeValue: SpecialAttributeDatabaseEditor = async (
where: {
alias_entry_id: vertex.dse.id,
aliased_entry_name: {
equals: decodedName.map(rdnToJson),
equals: _encode_DistinguishedName(decodedName, DER).toBytes(),
},
},
}));
Expand Down Expand Up @@ -146,7 +145,7 @@ const hasValue: SpecialAttributeValueDetector = async (
where: {
alias_entry_id: vertex.dse.id,
aliased_entry_name: {
equals: decodedName.map(rdnToJson),
equals: _encode_DistinguishedName(decodedName, DER).toBytes(),
},
},
}))
Expand Down
Loading