security: Add proper authentication, RBAC, and tenant isolation

- Add password hashing with bcrypt
- Add AuthService with proper login
- Add password strength validation
- Add RBAC middleware (AdminOnly, ManagerOrAdmin)
- Add tenant isolation middleware
- Update CRM handler with tenant filtering
- Add JWT fallback for development mode
- Add user context helpers
- Build successful
This commit is contained in:
Bernt
2026-08-10 12:52:48 +00:00
parent 8921fd1467
commit 78b57273e2
141 changed files with 29192 additions and 180 deletions
@@ -0,0 +1,162 @@
-- Migration 006: Landvex, Compliance, Analytics tables
-- Allt som tidigare var hårdkodat i Go flyttas till databas
-- Landvex: Entities (bolag)
CREATE TABLE IF NOT EXISTS boc_landvex_entities (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entity_id TEXT UNIQUE NOT NULL, -- t.ex. "lvx-ab", "lvx-inc"
name TEXT NOT NULL,
jurisdiction TEXT NOT NULL, -- SE, US, etc.
entity_type TEXT NOT NULL, -- holding, operating
status TEXT NOT NULL DEFAULT 'active',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Landvex: Ownership structure
CREATE TABLE IF NOT EXISTS boc_landvex_ownership (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entity_id TEXT NOT NULL REFERENCES boc_landvex_entities(entity_id),
owner_name TEXT NOT NULL,
owner_email TEXT,
ownership_percent NUMERIC(5,2) NOT NULL DEFAULT 100,
parent_entity_id TEXT REFERENCES boc_landvex_entities(entity_id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Landvex: Compliance items
CREATE TABLE IF NOT EXISTS boc_landvex_compliance (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entity_id TEXT NOT NULL REFERENCES boc_landvex_entities(entity_id),
category TEXT NOT NULL, -- tax, annual_report, audit, etc.
title TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending', -- pending, completed, overdue
due_date DATE,
completed_at TIMESTAMPTZ,
notes TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Compliance: Legal cases
CREATE TABLE IF NOT EXISTS boc_legal_cases (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
case_id TEXT UNIQUE NOT NULL, -- t.ex. "case-001"
entity_id TEXT NOT NULL REFERENCES boc_landvex_entities(entity_id),
title TEXT NOT NULL,
case_type TEXT NOT NULL, -- debt_collection, corporate, etc.
status TEXT NOT NULL DEFAULT 'active', -- active, pending, closed
priority TEXT NOT NULL DEFAULT 'medium', -- low, medium, high
description TEXT NOT NULL,
opposing_party TEXT NOT NULL,
lawyer TEXT,
opened_at DATE NOT NULL,
closed_at DATE,
value NUMERIC(15,2) DEFAULT 0,
currency TEXT DEFAULT 'SEK',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Analytics: Dashboard KPIs
CREATE TABLE IF NOT EXISTS boc_analytics_kpis (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
kpi_key TEXT UNIQUE NOT NULL, -- t.ex. "revenue_h1", "moms_att_betala"
label TEXT NOT NULL,
value NUMERIC(15,2) NOT NULL DEFAULT 0,
currency TEXT,
trend NUMERIC(5,2), -- procent, t.ex. 0.15 för 15%
period TEXT, -- t.ex. "H1 2026", "all"
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Analytics: Revenue trend (månadsvis)
CREATE TABLE IF NOT EXISTS boc_analytics_revenue (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
month TEXT NOT NULL, -- t.ex. "Jan", "Feb"
year INTEGER NOT NULL,
revenue NUMERIC(15,2) NOT NULL DEFAULT 0,
UNIQUE(year, month)
);
-- Analytics: Expenses by category
CREATE TABLE IF NOT EXISTS boc_analytics_expenses (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
category TEXT NOT NULL, -- t.ex. "IT/Molntjänster"
amount NUMERIC(15,2) NOT NULL DEFAULT 0,
period TEXT DEFAULT 'all',
UNIQUE(category, period)
);
-- Analytics: Alerts
CREATE TABLE IF NOT EXISTS boc_analytics_alerts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
alert_type TEXT NOT NULL, -- warning, info, danger
message TEXT NOT NULL,
due_date DATE,
dismissed BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Insert default Landvex data
INSERT INTO boc_landvex_entities (entity_id, name, jurisdiction, entity_type, status) VALUES
('lvx-ab', 'Landvex AB', 'SE', 'holding', 'active'),
('lvx-inc', 'Landvex Inc.', 'US', 'operating', 'active')
ON CONFLICT (entity_id) DO NOTHING;
-- Insert ownership
INSERT INTO boc_landvex_ownership (entity_id, owner_name, ownership_percent) VALUES
('lvx-ab', 'Erik Svensson', 100),
('lvx-inc', 'Landvex AB', 100)
ON CONFLICT DO NOTHING;
-- Insert compliance items
INSERT INTO boc_landvex_compliance (entity_id, category, title, status, due_date) VALUES
('lvx-ab', 'tax', 'Momsdeklaration H1 2026', 'pending', '2026-08-12'),
('lvx-ab', 'annual_report', 'Årsredovisning 2025', 'overdue', '2026-07-31'),
('lvx-ab', 'tax', 'Inkomstdeklaration 2025', 'overdue', '2026-05-02'),
('lvx-ab', 'annual_report', 'Årsredovisning 2024', 'completed', '2025-07-31'),
('lvx-ab', 'audit', 'Revisorns granskning 2025', 'pending', '2026-09-30'),
('lvx-inc', 'tax', 'Federal Tax Return 2025', 'pending', '2026-04-15')
ON CONFLICT DO NOTHING;
-- Insert legal cases
INSERT INTO boc_legal_cases (case_id, entity_id, title, case_type, status, priority, description, opposing_party, lawyer, opened_at, value, currency) VALUES
('case-001', 'lvx-ab', 'Leon Russo De Cerame — krav på återbetalning', 'debt_collection', 'active', 'high', 'Obehöriga uttag på företagskort, totalt 212 921,60 SEK', 'Leon Maurizio Russo De Cerame', 'Advokatfirman X', '2026-06-16', 212921.60, 'SEK')
ON CONFLICT (case_id) DO NOTHING;
-- Insert analytics KPIs
INSERT INTO boc_analytics_kpis (kpi_key, label, value, currency, trend, period) VALUES
('revenue_h1', 'Revenue H1 2026', 1856469.00, 'SEK', 0.15, 'H1 2026'),
('moms_att_betala', 'MOMS att betala', 440783.00, 'SEK', NULL, 'H1 2026'),
('customers_total', 'Customers', 3, NULL, NULL, 'all'),
('cash_on_hand', 'Cash on Hand', 45230.00, 'SEK', NULL, 'all')
ON CONFLICT (kpi_key) DO NOTHING;
-- Insert revenue trend
INSERT INTO boc_analytics_revenue (month, year, revenue) VALUES
('Jan', 2026, 811147),
('Feb', 2026, 0),
('Mar', 2026, 0),
('Apr', 2026, 955317),
('May', 2026, 0),
('Jun', 2026, 0)
ON CONFLICT (year, month) DO NOTHING;
-- Insert expenses by category
INSERT INTO boc_analytics_expenses (category, amount) VALUES
('IT/Molntjänster', 449984),
('Resekostnader', 276712),
('Representation', 84742),
('Externa tjänster', 152727),
('Löner', 100047),
('Övrigt', 45291)
ON CONFLICT (category, period) DO NOTHING;
-- Insert alerts
INSERT INTO boc_analytics_alerts (alert_type, message, due_date) VALUES
('warning', 'Momsdeklaration H1 2026 deadline: 12 augusti', '2026-08-12'),
('warning', 'Årsredovisning 2025 måste lämnas', '2026-07-31'),
('info', 'Inkomstdeklaration 2025 försenad', '2026-05-02')
ON CONFLICT DO NOTHING;
@@ -0,0 +1,84 @@
-- Migration 007: Visma eEkonomi features
-- Allt ett företag behöver
-- 1. LÖN (Payroll)
CREATE TABLE IF NOT EXISTS boc_payroll (
id SERIAL PRIMARY KEY,
employee_id TEXT NOT NULL,
period TEXT NOT NULL,
gross_salary NUMERIC(12,2) NOT NULL,
tax_deduction NUMERIC(12,2) NOT NULL,
employer_contribution NUMERIC(12,2) NOT NULL,
net_salary NUMERIC(12,2) NOT NULL,
payment_date DATE,
status TEXT DEFAULT 'draft',
created_at TIMESTAMP DEFAULT NOW()
);
-- 2. TID (Time tracking)
CREATE TABLE IF NOT EXISTS boc_time_entries (
id SERIAL PRIMARY KEY,
employee_id TEXT NOT NULL,
date DATE NOT NULL,
hours NUMERIC(4,2) NOT NULL,
project_id TEXT,
description TEXT,
billable BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT NOW()
);
-- 3. PROJEKT (redan skapad i 002, bara seed-data)
-- CREATE TABLE IF NOT EXISTS boc_projects (...); -- redan finns
-- 4. LAGER (Inventory)
CREATE TABLE IF NOT EXISTS boc_inventory (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
sku TEXT UNIQUE,
quantity INTEGER DEFAULT 0,
unit_cost NUMERIC(12,2),
unit_price NUMERIC(12,2),
category TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- 5. LEVERANTÖRER (Suppliers)
CREATE TABLE IF NOT EXISTS boc_suppliers (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
email TEXT,
phone TEXT,
org_number TEXT,
address TEXT,
payment_terms TEXT,
status TEXT DEFAULT 'active',
created_at TIMESTAMP DEFAULT NOW()
);
-- 6. INKÖP (Purchases)
CREATE TABLE IF NOT EXISTS boc_purchases (
id TEXT PRIMARY KEY,
supplier_id TEXT,
amount NUMERIC(12,2) NOT NULL,
currency TEXT DEFAULT 'SEK',
status TEXT DEFAULT 'draft',
due_date DATE,
created_at TIMESTAMP DEFAULT NOW()
);
-- 7. RAPPORTER (Reports)
CREATE TABLE IF NOT EXISTS boc_reports (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
type TEXT NOT NULL,
period TEXT,
data JSONB,
created_at TIMESTAMP DEFAULT NOW()
);
-- Seed data
-- INSERT INTO boc_projects (...) -- redan finns data
-- INSERT INTO boc_inventory (...) -- redan finns data
-- INSERT INTO boc_suppliers (...) -- redan finns data
-- INSERT INTO boc_purchases (...) -- redan finns data
@@ -0,0 +1,17 @@
-- =====================================================
-- Migration 008: Add password hash for secure authentication
-- =====================================================
-- Lägg till password_hash för säker lösenordslagring
ALTER TABLE boc_users ADD COLUMN IF NOT EXISTS password_hash TEXT;
-- Skapa index för snabb login-lookup
CREATE INDEX IF NOT EXISTS idx_users_email ON boc_users(email);
-- Uppdatera befintliga användare med default lösenord (byt omedelbart!)
-- Default: 'changeme' - bcrypt hash
UPDATE boc_users
SET password_hash = '$2a$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewKyNiAYMyzJ/I2K'
WHERE password_hash IS NULL;
-- Kommentar: Ovanstående hash är för 'changeme' - ALLA användare måste byta lösenord!
+225
View File
@@ -0,0 +1,225 @@
-- Seed contract templates for ISO 9001 and common business contracts
INSERT INTO boc_contract_templates (id, name, type, category, content, placeholders, created_at, updated_at) VALUES
('tmpl-iso-9001-1', 'ISO 9001:2015 Kvalitetsledningssystem', 'iso', 'quality',
E'KVALITETSPOLICY
{{company_name}} ska leverera produkter och tjänster som uppfyller kundernas krav och förväntningar samt tillämpliga lagkrav och andra krav.
KVALITETSLEDNINGSSYSTEM
{{company_name}} har upprättat, dokumenterat, implementerat och underhåller ett kvalitetsledningssystem i enlighet med kraven i ISO 9001:2015.
ANSVAR OCH MYNDIGHET
Kvalitetsansvarig: {{quality_manager}}
Datum: {{date}}
Giltig till: {{expiry_date}}
{{company_name}}
Org.nr: {{org_number}}
Adress: {{address}}
Underskrift: ___________________',
'["company_name", "quality_manager", "date", "expiry_date", "org_number", "address"]',
NOW(), NOW()),
('tmpl-iso-9001-2', 'ISO 9001:2015 Internrevision', 'iso', 'quality',
E'INTERNREVISIONSPROGRAM
Företag: {{company_name}}
Revisionsansvarig: {{auditor}}
Datum: {{date}}
1. SYFTE
Verifiera att kvalitetsledningssystemet:
- Upfyller planerade arrangemang
- Upfyller kraven i ISO 9001:2015
- Är effektivt implementerat och underhållet
2. OMFATTNING
{{scope}}
3. REFERENSER
- ISO 9001:2015
- Kvalitetshandbok
- Tillämpliga procedurer
4. REVISIONSRESULTAT
{{findings}}
5. ÅTGÄRDER
{{actions}}
Godkänd av: {{approver}}
Datum: {{approval_date}}',
'["company_name", "auditor", "date", "scope", "findings", "actions", "approver", "approval_date"]',
NOW(), NOW()),
('tmpl-iso-9001-3', 'ISO 9001:2015 Korrigerande åtgärd', 'iso', 'quality',
E'KORRIGERANDE ÅTGÄRDSRAPPORT (CAR)
CAR-nummer: {{car_number}}
Datum: {{date}}
Rapporterad av: {{reporter}}
1. BESKRIVNING AV AVVIKELSE
{{deviation_description}}
2. ROTORSAKSANALYS
{{root_cause}}
3. KORRIGERANDE ÅTGÄRD
{{corrective_action}}
4. FÖREBYGGANDE ÅTGÄRD
{{preventive_action}}
5. VERIFIERING
Verifierad av: {{verifier}}
Datum: {{verification_date}}
Resultat: {{verification_result}}',
'["car_number", "date", "reporter", "deviation_description", "root_cause", "corrective_action", "preventive_action", "verifier", "verification_date", "verification_result"]',
NOW(), NOW()),
('tmpl-employment-1', 'Anställningsavtal', 'employment', 'hr',
E'ANSTÄLLNINGSAVTAL
1. PARTER
Arbetsgivare: {{employer_name}} (org.nr {{employer_org}})
Arbetstagare: {{employee_name}} (personnr {{employee_ssn}})
2. ANSTÄLLNING
Befattning: {{position}}
Avdelning: {{department}}
Anställningsform: {{employment_type}}
Startdatum: {{start_date}}
3. LÖN OCH FÖRMÅNER
Månadslön: {{salary}} {{currency}}
Semester: {{vacation_days}} dagar/år
Arbetstid: {{working_hours}}
4. UPPSÄGNING
Uppsägningstid: {{notice_period}}
5. ÖVRIGT
{{additional_terms}}
Ort och datum: {{place_date}}
Arbetsgivarens underskrift: ___________________
Arbetstagarens underskrift: ___________________',
'["employer_name", "employer_org", "employee_name", "employee_ssn", "position", "department", "employment_type", "start_date", "salary", "currency", "vacation_days", "working_hours", "notice_period", "additional_terms", "place_date"]',
NOW(), NOW()),
('tmpl-nda-1', 'Sekretessavtal (NDA)', 'legal', 'confidentiality',
E'SEKRETESSAVTAL
1. PARTER
Uppdragsgivare: {{party_a}}
Mottagare: {{party_b}}
Datum: {{date}}
2. SYFTE
Part B ska få tillgång till konfidentiell information från Part A i syfte att {{purpose}}.
3. DEFINITION AV KONFIDENTIELL INFORMATION
Konfidentiell information inkluderar men är inte begränsat till:
- Affärsplaner och strategier
- Teknisk dokumentation
- Kundlistor och prisinformation
- Programkod och algoritmer
4. SKYLDIGHETER
Mottagaren förbinder sig att:
- Inte avslöja konfidentiell information för tredje part
- Inte använda informationen för andra syften än avtalat
- Vidta rimliga säkerhetsåtgärder
5. GILTIGHETSTID
{{validity_years}} år från avtalets undertecknande.
6. PÅFÖLJD
Vid brott mot detta avtal utgår vite om {{penalty}} {{currency}}.
Underskrifter:
{{party_a}}: ___________________ {{party_b}}: ___________________',
'["party_a", "party_b", "date", "purpose", "validity_years", "penalty", "currency"]',
NOW(), NOW()),
('tmpl-service-1', 'Tjänsteavtal (SLA)', 'service', 'operations',
E'TJÄNSTEAVTAL
1. PARTER
Leverantör: {{provider}}
Kund: {{customer}}
Avtalsnummer: {{contract_number}}
2. TJÄNSTER
{{services_description}}
3. SERVICENIVÅER (SLA)
- Tillgänglighet: {{availability}}%
- Responstid: {{response_time}} timmar
- Återställningstid: {{recovery_time}} timmar
4. PRISER OCH BETALNING
Månadsavgift: {{monthly_fee}} {{currency}}
Betalningsvillkor: {{payment_terms}} dagar
5. AVTALSTID
Start: {{start_date}}
Slut: {{end_date}}
Uppsägningstid: {{notice_period}} månader
6. KONTAKTPERSONER
Leverantör: {{provider_contact}}
Kund: {{customer_contact}}
Underskrifter:
{{provider}}: ___________________ {{customer}}: ___________________',
'["provider", "customer", "contract_number", "services_description", "availability", "response_time", "recovery_time", "monthly_fee", "currency", "payment_terms", "start_date", "end_date", "notice_period", "provider_contact", "customer_contact"]',
NOW(), NOW()),
('tmpl-gdpr-1', 'GDPR Personuppgiftsbiträdesavtal', 'gdpr', 'privacy',
E'PERSONUPPGIFTSBITRÄDESAVTAL
1. PARTER
Personuppgiftsansvarig: {{data_controller}}
Personuppgiftsbiträde: {{data_processor}}
Datum: {{date}}
2. BEHANDLING
Personuppgiftsbiträdet ska behandla personuppgifter för följande ändamål:
{{processing_purpose}}
3. KATEGORIER AV REGISTERFÖRDA
{{data_subjects}}
4. TYP AV PERSONUPPGIFTER
{{data_types}}
5. SÄKERHETSÅTGÄRDER
Biträdet ska implementera följande tekniska och organisatoriska åtgärder:
{{security_measures}}
6. UNDERLEVERANTÖRER
Godkända underleverantörer: {{subprocessors}}
7. AVTALSTID OCH UPPSÄGNING
Giltig från: {{start_date}}
Uppsägningstid: {{notice_period}} månader
Underskrifter:
{{data_controller}}: ___________________ {{data_processor}}: ___________________',
'["data_controller", "data_processor", "date", "processing_purpose", "data_subjects", "data_types", "security_measures", "subprocessors", "start_date", "notice_period"]',
NOW(), NOW());
-- Seed sample contracts
INSERT INTO boc_contracts (id, template_type, name, counterparty, counterparty_org, status, value, currency, start_date, end_date, responsible, created_at, updated_at) VALUES
('ctr-001', 'tmpl-employment-1', 'Anställningsavtal - Erik Svensson', 'Erik Svensson', 'LandveX AB', 'active', 0, 'SEK', '2024-01-01', NULL, 'Erik Svensson', NOW(), NOW()),
('ctr-002', 'tmpl-employment-1', 'Anställningsavtal - Johan Berglund', 'Johan Berglund', 'LandveX AB', 'active', 0, 'SEK', '2024-01-01', NULL, 'Erik Svensson', NOW(), NOW()),
('ctr-003', 'tmpl-iso-9001-1', 'ISO 9001:2015 Kvalitetscertifiering', 'LandveX AB', 'LandveX AB', 'active', 150000, 'SEK', '2024-01-01', '2027-01-01', 'Erik Svensson', NOW(), NOW()),
('ctr-004', 'tmpl-service-1', 'AWS Hosting SLA', 'Amazon Web Services', 'AWS', 'active', 50000, 'USD', '2024-01-01', '2025-01-01', 'Johan Berglund', NOW(), NOW()),
('ctr-005', 'tmpl-nda-1', 'Sekretessavtal - Atlas Capture', 'Jun Wakabayashi', 'Atlas Capture', 'active', 0, 'SEK', '2024-07-03', '2026-07-03', 'Erik Svensson', NOW(), NOW());