Restrict registration credentials by portal type and simplify vault form

This commit is contained in:
A R R R Associates
2026-09-02 15:17:39 +05:30
parent 7702cf8f6b
commit 1ce2fd070e
2 changed files with 159 additions and 46 deletions
@@ -4,55 +4,109 @@
<div class="mb-5">
<h1 class="text-2xl font-bold">Add encrypted credential</h1>
{% if selected_registration %}
<p class="mt-1 text-sm text-slate-600">Linked to registration <strong>{{ selected_registration.registration_number }}</strong>. Secrets remain encrypted and reveal access is audited.</p>
<p class="mt-1 text-sm text-slate-600">
Add a portal credential for this registration. Secrets remain encrypted and every reveal is audited.
</p>
{% else %}
<p class="mt-1 text-sm text-slate-600">Add a firm or client credential. Secrets remain encrypted and every reveal is audited.</p>
{% endif %}
</div>
<form method="post" class="grid gap-4 rounded-xl bg-white p-6 shadow sm:grid-cols-2">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<label class="sm:col-span-2">Title<input required name="title" class="mt-1 w-full rounded-lg border p-2" placeholder="e.g. GST Portal - Main Login"></label>
<label>Credential type
<select name="category" class="mt-1 w-full rounded-lg border p-2">
<option value="gst_portal">GST Portal</option>
<option value="income_tax_portal">Income Tax Portal</option>
<option value="traces_tds">TRACES / TDS</option>
<option value="mca_portal">MCA Portal</option>
<option value="eway_bill">E-Way Bill</option>
<option value="einvoice">E-Invoice</option>
<option value="government_portal">Other Government Portal</option>
<option value="banking">Banking</option>
<option value="email">Email</option>
<option value="software">Software</option>
<option value="api_key">API key</option>
<option value="digital_signature">Digital signature</option>
<option value="other">Other</option>
</select>
</label>
<label>Sensitivity<select name="sensitivity" class="mt-1 w-full rounded-lg border p-2"><option>high</option><option>critical</option><option>standard</option></select></label>
<label>Client
<select name="client_id" id="vault-client" class="mt-1 w-full rounded-lg border p-2">
<option value="">Firm-level credential</option>
{% for c in clients %}<option value="{{ c.id }}" {% if selected_client_id and c.id == selected_client_id %}selected{% endif %}>{{ c.client_name }}</option>{% endfor %}
</select>
</label>
<label>Registration record
<select name="registration_id" id="vault-registration" class="mt-1 w-full rounded-lg border p-2">
<option value="">Not linked</option>
{% for r in registrations %}<option value="{{ r.id }}" data-client-id="{{ r.client_id }}" {% if selected_registration_id and r.id == selected_registration_id %}selected{% endif %}>{{ r.registration_number }}</option>{% endfor %}
</select>
</label>
<label class="sm:col-span-2">Portal URL<input name="portal_url" type="url" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Reference number<input name="reference_number" class="mt-1 w-full rounded-lg border p-2" placeholder="GSTIN / TAN / CIN / login reference"></label>
<label>Username/login ID<input name="username" autocomplete="off" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Secret/password/token<input required name="secret" type="password" autocomplete="new-password" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Additional secret/PIN<input name="additional_secret" type="password" autocomplete="new-password" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Expires on<input name="expires_on" type="date" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Rotation due on<input name="rotation_due_on" type="date" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Owner<select name="owner_user_id" class="mt-1 w-full rounded-lg border p-2"><option value="">Current user</option>{% for u in users %}<option value="{{ u.id }}">{{ u.full_name or u.email }}</option>{% endfor %}</select></label>
<fieldset><legend>Explicit staff access</legend><div class="mt-1 max-h-32 overflow-auto rounded-lg border p-2">{% for u in users %}<label class="block text-sm"><input type="checkbox" name="allowed_user_ids" value="{{ u.id }}"> {{ u.full_name or u.email }}</label>{% endfor %}</div></fieldset>
<label class="sm:col-span-2">Encrypted notes<textarea name="notes" rows="3" class="mt-1 w-full rounded-lg border p-2"></textarea></label>
<div class="sm:col-span-2 flex gap-2"><button class="rounded-lg bg-brand-600 px-4 py-2 font-semibold text-white">Save securely</button><a href="/credential-vault" class="rounded-lg border px-4 py-2">Cancel</a></div>
{% if selected_registration %}
<input type="hidden" name="client_id" value="{{ selected_registration.client_id }}">
<input type="hidden" name="registration_id" value="{{ selected_registration.id }}">
<input type="hidden" name="sensitivity" value="high">
<div class="rounded-lg border bg-slate-50 p-3">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Client</div>
<div class="mt-1 font-semibold text-slate-900">{{ selected_client.client_name if selected_client else selected_registration.client_id }}</div>
</div>
<div class="rounded-lg border bg-slate-50 p-3">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Registration</div>
<div class="mt-1 font-semibold text-slate-900">{{ selected_registration.registration_number }}</div>
<div class="mt-1 text-xs text-slate-500">{{ selected_registration_type.name if selected_registration_type else 'Registration' }}</div>
</div>
<label>Portal / Credential type
<select name="category" required class="mt-1 w-full rounded-lg border p-2">
{% for code, label in credential_types %}
<option value="{{ code }}">{{ label }}</option>
{% endfor %}
</select>
<span class="mt-1 block text-xs text-slate-500">Only portals valid for this registration type are shown.</span>
</label>
<label>Title
<input required name="title" class="mt-1 w-full rounded-lg border p-2" placeholder="e.g. Main login / Deductor login">
</label>
<label>Username / Login ID
<input name="username" autocomplete="off" class="mt-1 w-full rounded-lg border p-2">
</label>
<label>Secret / Password / Token
<input required name="secret" type="password" autocomplete="new-password" class="mt-1 w-full rounded-lg border p-2">
</label>
<label>Additional PIN / Secret <span class="text-xs text-slate-500">(optional)</span>
<input name="additional_secret" type="password" autocomplete="new-password" class="mt-1 w-full rounded-lg border p-2">
</label>
<label class="sm:col-span-2">Remarks <span class="text-xs text-slate-500">(optional)</span>
<textarea name="notes" rows="3" class="mt-1 w-full rounded-lg border p-2"></textarea>
</label>
<!-- Keep the existing POST contract without exposing irrelevant generic-vault fields. -->
<input type="hidden" name="portal_url" value="">
<input type="hidden" name="reference_number" value="{{ selected_registration.registration_number }}">
<input type="hidden" name="expires_on" value="">
<input type="hidden" name="rotation_due_on" value="">
<input type="hidden" name="owner_user_id" value="{{ current_user.id }}">
{% else %}
<label class="sm:col-span-2">Title<input required name="title" class="mt-1 w-full rounded-lg border p-2" placeholder="e.g. GST Portal - Main Login"></label>
<label>Credential type
<select name="category" class="mt-1 w-full rounded-lg border p-2">
{% for code, label in credential_types %}<option value="{{ code }}">{{ label }}</option>{% endfor %}
</select>
</label>
<label>Sensitivity<select name="sensitivity" class="mt-1 w-full rounded-lg border p-2"><option>high</option><option>critical</option><option>standard</option></select></label>
<label>Client
<select name="client_id" id="vault-client" class="mt-1 w-full rounded-lg border p-2">
<option value="">Firm-level credential</option>
{% for c in clients %}<option value="{{ c.id }}" {% if selected_client_id and c.id == selected_client_id %}selected{% endif %}>{{ c.client_name }}</option>{% endfor %}
</select>
</label>
<label>Registration record
<select name="registration_id" id="vault-registration" class="mt-1 w-full rounded-lg border p-2">
<option value="">Not linked</option>
{% for r in registrations %}<option value="{{ r.id }}" data-client-id="{{ r.client_id }}" {% if selected_registration_id and r.id == selected_registration_id %}selected{% endif %}>{{ r.registration_number }}</option>{% endfor %}
</select>
</label>
<label class="sm:col-span-2">Portal URL<input name="portal_url" type="url" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Reference number<input name="reference_number" class="mt-1 w-full rounded-lg border p-2" placeholder="GSTIN / TAN / CIN / login reference"></label>
<label>Username/login ID<input name="username" autocomplete="off" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Secret/password/token<input required name="secret" type="password" autocomplete="new-password" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Additional secret/PIN<input name="additional_secret" type="password" autocomplete="new-password" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Expires on<input name="expires_on" type="date" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Rotation due on<input name="rotation_due_on" type="date" class="mt-1 w-full rounded-lg border p-2"></label>
<label>Owner<select name="owner_user_id" class="mt-1 w-full rounded-lg border p-2"><option value="">Current user</option>{% for u in users %}<option value="{{ u.id }}">{{ u.full_name or u.email }}</option>{% endfor %}</select></label>
<fieldset><legend>Explicit staff access</legend><div class="mt-1 max-h-32 overflow-auto rounded-lg border p-2">{% for u in users %}<label class="block text-sm"><input type="checkbox" name="allowed_user_ids" value="{{ u.id }}"> {{ u.full_name or u.email }}</label>{% endfor %}</div></fieldset>
<label class="sm:col-span-2">Encrypted notes<textarea name="notes" rows="3" class="mt-1 w-full rounded-lg border p-2"></textarea></label>
{% endif %}
<div class="sm:col-span-2 flex gap-2">
<button class="rounded-lg bg-brand-600 px-4 py-2 font-semibold text-white">Save securely</button>
{% if selected_registration %}
<a href="/registrations/client/{{ selected_registration.client_id }}" class="rounded-lg border px-4 py-2">Cancel</a>
{% else %}
<a href="/credential-vault" class="rounded-lg border px-4 py-2">Cancel</a>
{% endif %}
</div>
</form>
</div>
{% if not selected_registration %}
<script>
(() => {
const client = document.getElementById('vault-client');
@@ -71,4 +125,5 @@
filterRegistrations();
})();
</script>
{% endif %}
{% endblock %}