/* Requires PostgreSQL 9.5 or later. * From PostgreSQL documentation: ON CONFLICT clause is only available from PostgreSQL 9.5 */ CREATE TABLE devices ( id VARCHAR(255) NOT NULL, identity_cert TEXT NULL, serial_number VARCHAR(127) NULL, -- If the (iOS, iPadOS) device sent an UnlockToken in the TokenUpdate -- TODO: Consider using a TEXT field and encoding the binary unlock_token BYTEA NULL, unlock_token_at TIMESTAMP NULL, -- The last raw Authenticate for this device authenticate TEXT NOT NULL, authenticate_at TIMESTAMP NOT NULL, -- The last raw TokenUpdate for this device token_update TEXT NULL, token_update_at TIMESTAMP NULL, bootstrap_token_b64 TEXT NULL, bootstrap_token_at TIMESTAMP NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- trigger PRIMARY KEY (id), CHECK (identity_cert IS NULL OR SUBSTRING(identity_cert FROM 1 FOR 27) = '-----BEGIN CERTIFICATE-----'), CHECK (serial_number IS NULL OR serial_number != ''), CHECK (unlock_token IS NULL OR LENGTH(unlock_token) > 0), CHECK (authenticate != ''), CHECK (token_update IS NULL OR token_update != ''), CHECK (bootstrap_token_b64 IS NULL OR bootstrap_token_b64 != '') ); CREATE INDEX serial_number ON devices (serial_number); CREATE TABLE users ( id VARCHAR(255) NOT NULL, device_id VARCHAR(255) NOT NULL, user_short_name VARCHAR(255) NULL, user_long_name VARCHAR(255) NULL, -- The last raw TokenUpdate for this user token_update TEXT NULL, token_update_at TIMESTAMP NULL, -- The last raw UserAuthenticate (and optional digest) for this user user_authenticate TEXT NULL, user_authenticate_at TIMESTAMP NULL, user_authenticate_digest TEXT NULL, user_authenticate_digest_at TIMESTAMP NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- trigger PRIMARY KEY (id, device_id), UNIQUE (id), FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE ON UPDATE CASCADE, CHECK (user_short_name IS NULL OR user_short_name != ''), CHECK (user_long_name IS NULL OR user_long_name != ''), CHECK (token_update IS NULL OR token_update != ''), CHECK (user_authenticate IS NULL OR user_authenticate != ''), CHECK (user_authenticate_digest IS NULL OR user_authenticate_digest != '') ); /* This table represents enrollments which are an amalgamation of * both device and user enrollments. */ CREATE TABLE enrollments ( -- The enrollment ID of this enrollment id VARCHAR(255) NOT NULL, -- The "device" enrollment ID of this enrollment. This will be -- the same as the `id` field in the case of a "device" enrollment, -- or will be the "parent" enrollment for a "user" enrollment. device_id VARCHAR(255) NOT NULL, -- The "user" enrollment ID of this enrollment. This will be the -- same as the `id` field in the case of a "user" enrollment or -- NULL in the case of a device enrollment. user_id VARCHAR(255) NULL, -- Textual representation of the type of device enrollment. type VARCHAR(31) NOT NULL, -- The MDM APNs push trifecta. topic VARCHAR(255) NOT NULL, push_magic VARCHAR(127) NOT NULL, token_hex VARCHAR(255) NOT NULL, -- TODO: Perhaps just CHAR(64)? enabled BOOLEAN NOT NULL DEFAULT TRUE, token_update_tally INTEGER NOT NULL DEFAULT 1, last_seen_at TIMESTAMP NOT NULL, -- TODO: additional tests with real device and integration tests. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), CHECK (id != ''), FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE, UNIQUE (user_id), CHECK (type != ''), CHECK (topic != ''), CHECK (push_magic != ''), CHECK (token_hex != '') ); CREATE INDEX idx_type ON enrollments (type); /* Commands stand alone. By themselves they aren't associated with * a device, a result (response), etc. Joining other tables is required * for more context. */ CREATE TABLE commands ( command_uuid VARCHAR(127) NOT NULL, request_type VARCHAR(63) NOT NULL, -- Raw command Plist command TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (command_uuid), CHECK (command_uuid != ''), CHECK (request_type != ''), CHECK (SUBSTRING(command FROM 1 FOR 5) = '