test: add comprehensive integration test suite for IAM platform
Add 162 integration tests covering authentication flows, TOTP MFA, SSH key/certificate management, organization workflows, multi-org access, self-service features, admin operations, authorization, security edge cases, department/principal management, CA management, policy compliance, WebAuthn passkeys, and ZeroTier network access. Includes: - Reusable API client library with session management - Test fixtures for users, organizations, memberships, and CAs - Helper functions for SSH key generation and verification - Documentation for running and writing tests Also update test configuration to disable conflicting maas plugins and configure WebAuthn/session settings for localhost testing.
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
import pytest
|
||||
from datetime import datetime, timezone
|
||||
from gatehouse_app.extensions import db
|
||||
from gatehouse_app.models.user.user import User
|
||||
from gatehouse_app.models.organization.organization import Organization
|
||||
from gatehouse_app.models.organization.organization_member import OrganizationMember
|
||||
from gatehouse_app.models.ssh_ca.ca import CA, CaType, KeyType
|
||||
from gatehouse_app.api.v1.ssh._helpers import _get_org_ca_for_user
|
||||
from gatehouse_app.utils.constants import OrganizationRole
|
||||
|
||||
|
||||
class TestCASoftDelete:
|
||||
"""Test CA soft delete handling."""
|
||||
|
||||
def test_active_ca_is_returned(self, app, test_user, test_org, test_ca, test_membership):
|
||||
"""Active CA should be returned."""
|
||||
with app.app_context():
|
||||
user = db.session.get(User, test_user)
|
||||
ca = _get_org_ca_for_user(user, ca_type='user')
|
||||
assert ca is not None
|
||||
assert ca.id == test_ca
|
||||
|
||||
def test_deleted_ca_is_not_returned(self, app, test_user, test_org, test_membership):
|
||||
"""Deleted CA should not be returned."""
|
||||
with app.app_context():
|
||||
ca = CA(
|
||||
organization_id=test_org,
|
||||
name='Deleted CA',
|
||||
ca_type=CaType.USER,
|
||||
key_type=KeyType.ED25519,
|
||||
private_key='key',
|
||||
public_key='pubkey',
|
||||
fingerprint='sha256:deleted123',
|
||||
is_active=True,
|
||||
deleted_at=datetime.now(timezone.utc)
|
||||
)
|
||||
db.session.add(ca)
|
||||
db.session.commit()
|
||||
|
||||
user = db.session.get(User, test_user)
|
||||
result = _get_org_ca_for_user(user, ca_type='user')
|
||||
assert result is None
|
||||
|
||||
def test_deleted_membership_no_access(self, app, test_org, test_ca):
|
||||
"""User with deleted membership should not access CA."""
|
||||
with app.app_context():
|
||||
user = User(email='deleted_member@test.com', full_name='Deleted Member')
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
membership = OrganizationMember(
|
||||
user_id=user.id,
|
||||
organization_id=test_org,
|
||||
role=OrganizationRole.MEMBER,
|
||||
deleted_at=datetime.now(timezone.utc)
|
||||
)
|
||||
db.session.add(membership)
|
||||
db.session.commit()
|
||||
|
||||
result = _get_org_ca_for_user(user, ca_type='user')
|
||||
assert result is None
|
||||
|
||||
def test_deleted_org_no_access(self, app):
|
||||
"""User in deleted org should not access CA."""
|
||||
with app.app_context():
|
||||
org = Organization(
|
||||
name='Deleted Org',
|
||||
slug='deleted-org',
|
||||
deleted_at=datetime.now(timezone.utc)
|
||||
)
|
||||
db.session.add(org)
|
||||
db.session.commit()
|
||||
|
||||
user = User(email='user@deleted.org', full_name='User')
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
membership = OrganizationMember(
|
||||
user_id=user.id,
|
||||
organization_id=org.id,
|
||||
role=OrganizationRole.MEMBER
|
||||
)
|
||||
db.session.add(membership)
|
||||
|
||||
ca = CA(
|
||||
organization_id=org.id,
|
||||
name='CA in Deleted Org',
|
||||
ca_type=CaType.USER,
|
||||
key_type=KeyType.ED25519,
|
||||
private_key='key',
|
||||
public_key='pubkey',
|
||||
fingerprint='sha256:deletedorg123',
|
||||
is_active=True
|
||||
)
|
||||
db.session.add(ca)
|
||||
db.session.commit()
|
||||
|
||||
result = _get_org_ca_for_user(user, ca_type='user')
|
||||
assert result is None
|
||||
|
||||
def test_get_active_memberships_excludes_deleted(self, app, test_user, test_org, test_membership):
|
||||
"""User.get_active_memberships() should exclude deleted memberships."""
|
||||
with app.app_context():
|
||||
user = db.session.get(User, test_user)
|
||||
|
||||
org2 = Organization(name='Org 2', slug='org-2')
|
||||
db.session.add(org2)
|
||||
db.session.commit()
|
||||
|
||||
membership2 = OrganizationMember(
|
||||
user_id=test_user,
|
||||
organization_id=org2.id,
|
||||
role=OrganizationRole.MEMBER,
|
||||
deleted_at=datetime.now(timezone.utc)
|
||||
)
|
||||
db.session.add(membership2)
|
||||
db.session.commit()
|
||||
|
||||
active = user.get_active_memberships()
|
||||
assert len(active) == 1
|
||||
assert active[0].organization_id == test_org
|
||||
|
||||
def test_get_organizations_excludes_deleted(self, app, test_user, test_org, test_membership):
|
||||
"""User.get_organizations() should exclude deleted memberships/orgs."""
|
||||
with app.app_context():
|
||||
user = db.session.get(User, test_user)
|
||||
|
||||
org2 = Organization(name='Deleted Org', slug='deleted-org-2')
|
||||
db.session.add(org2)
|
||||
db.session.commit()
|
||||
|
||||
membership2 = OrganizationMember(
|
||||
user_id=test_user,
|
||||
organization_id=org2.id,
|
||||
role=OrganizationRole.MEMBER,
|
||||
deleted_at=datetime.now(timezone.utc)
|
||||
)
|
||||
db.session.add(membership2)
|
||||
db.session.commit()
|
||||
|
||||
orgs = user.get_organizations()
|
||||
assert len(orgs) == 1
|
||||
assert orgs[0].id == test_org
|
||||
Reference in New Issue
Block a user