initial schema for user authentication (#26)

This is only the database schema, which I'm adding now in the hopes of
freezing schema version 3. There's no way yet to create users, much less
actually authenticate.
This commit is contained in:
Scott Lamb 2018-03-21 23:55:21 -07:00
parent dfee66c84b
commit 1c9f2a4d83
3 changed files with 95 additions and 0 deletions

View File

@ -298,5 +298,73 @@ create table video_sample_entry (
data blob not null check (length(data) > 86)
);
create table user (
id integer primary key,
username unique not null,
-- Bitwise mask of flags:
-- 1: disabled. If set, no method of authentication for this user will succeed.
flags integer not null,
-- If set, a hash for password authentication, as generated by `libpasta::hash_password`.
password_hash text,
-- A counter which increments with every password reset or clear.
password_id integer not null default 0,
-- Updated lazily on database flush; reset when password_id is incremented.
-- This could be used to automatically disable the password on hitting a threshold.
password_failure_count integer not null,
-- If set, a Unix UID that is accepted for authentication when using HTTP over
-- a Unix domain socket. (Additionally, the UID running Moonfire NVR can authenticate
-- as anyone; there's no point in trying to do otherwise.) This might be an easy
-- bootstrap method once configuration happens through a web UI rather than text UI.
unix_uid integer
);
-- A single session, whether for browser or robot use.
create table user_session (
-- The session id is a 48-byte blob (which is base64 encoded to 64 bytes in the HTTP cookie).
-- This is the unencoded SHA3-256 of the unencoded session id. Much like `password_hash`,
-- a hash is used here so that a leaked database backup can't be trivially used to steal
-- credentials. The hash is unsalted; unlike passwords designed for humans to
-- remember, the session id is assumed to itself have sufficient entropy.
session_id_hash blob primary key not null,
user_id integer references user (id),
-- A bitwise mask of flags, currently all properties of the HTTP cookie used to hold the session:
-- 1: HttpOnly
-- 2: Secure
-- 4: SameSite (lax)
flags integer not null,
-- The domain of the HTTP cookie used to store this session. The outbound
-- `Set-Cookie` header never specifies a scope, so this matches the `Host:` of
-- the inbound HTTP request.
domain text,
-- An editable description which might describe the device/program which uses
-- this session, such as "Chromebook", "iPhone", or "motion detection worker".
description text,
creation_password_id integer, -- the id it was created from, if created via password
creation_peer_addr blob, -- IPv4 or IPv6 address, or null for Unix socket.
creation_time_sec integer not null, -- sec since epoch
creation_user_agent text, -- User-Agent header from inbound HTTP request.
revocation_time_sec integer, -- sec since epoch
revocation_reason text,
-- Information about requests which used this session, updated lazily on database flush.
last_use_time_sec integer, -- sec since epoch
last_use_user_agent text, -- User-Agent header from inbound HTTP request.
last_use_peer_addr blob, -- IPv4 or IPv6 address, or null for Unix socket.
use_count not null
) without rowid;
create index user_session_uid on user_session (user_id);
insert into version (id, unix_time, notes)
values (3, cast(strftime('%s', 'now') as int), 'db creation');

View File

@ -67,6 +67,32 @@ pub fn run(args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
uuid blob unique not null check (length(uuid) = 16),
last_complete_open_id integer references open (id)
);
create table user (
id integer primary key,
username unique not null,
flags integer not null,
password_hash text,
password_id integer not null default 0,
password_failure_count integer not null,
unix_uid integer
);
create table user_session (
session_id_hash blob primary key not null,
user_id integer references user (id),
flags integer not null,
domain text,
description text,
creation_password_id integer,
creation_peer_addr blob,
creation_time_sec integer not null,
creation_user_agent text,
revocation_time_sec integer,
revocation_reason text,
last_use_time_sec integer,
last_use_user_agent text,
last_use_peer_addr blob,
use_count not null
) without rowid;
"#)?;
let db_uuid = ::uuid::Uuid::new_v4();
let db_uuid_bytes = &db_uuid.as_bytes()[..];

View File

@ -206,6 +206,7 @@ is never used.
Version 3 adds over version 1:
* user authentication
* recording of sub streams (splits a new `stream` table out of `camera`)
* a per-stream knob `flush_if_sec` meant to reduce database commits (and
thus SSD write cycles). This improves practicality of many streams.