Merge pull request #1147 from dod-ccpo/disable-user
Implement EnvironmentRoles.disable
This commit is contained in:
commit
36dc66504c
@ -0,0 +1,57 @@
|
|||||||
|
"""add disabled enviornment_role status
|
||||||
|
|
||||||
|
Revision ID: f328f1ea400c
|
||||||
|
Revises: e05d1f2682af
|
||||||
|
Create Date: 2019-10-29 15:16:04.037436
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'f328f1ea400c' # pragma: allowlist secret
|
||||||
|
down_revision = 'e05d1f2682af' # pragma: allowlist secret
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.alter_column(
|
||||||
|
"environment_roles",
|
||||||
|
"status",
|
||||||
|
type_=sa.Enum(
|
||||||
|
"PENDING",
|
||||||
|
"COMPLETED",
|
||||||
|
"DISABLED",
|
||||||
|
name="status",
|
||||||
|
native_enum=False,
|
||||||
|
),
|
||||||
|
existing_type=sa.Enum(
|
||||||
|
"PENDING", "COMPLETED", "PENDING_DELETE", name="status", native_enum=False
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
conn = op.get_bind()
|
||||||
|
conn.execute(
|
||||||
|
"""
|
||||||
|
UPDATE environment_roles
|
||||||
|
SET status = (CASE WHEN status = 'DISABLED' THEN 'PENDING_DELETE' ELSE status END)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
op.alter_column(
|
||||||
|
"environment_roles",
|
||||||
|
"status",
|
||||||
|
type_=sa.Enum(
|
||||||
|
"PENDING", "COMPLETED", "PENDING_DELETE", name="status", native_enum=False
|
||||||
|
),
|
||||||
|
existing_type=sa.Enum(
|
||||||
|
"PENDING",
|
||||||
|
"COMPLETED",
|
||||||
|
"DISABLED",
|
||||||
|
name="status",
|
||||||
|
native_enum=False,
|
||||||
|
),
|
||||||
|
)
|
@ -6,7 +6,10 @@ from .reports import MockReportingProvider
|
|||||||
class MockCSP:
|
class MockCSP:
|
||||||
def __init__(self, app, test_mode=False):
|
def __init__(self, app, test_mode=False):
|
||||||
self.cloud = MockCloudProvider(
|
self.cloud = MockCloudProvider(
|
||||||
app.config, with_delay=(not test_mode), with_failure=(not test_mode)
|
app.config,
|
||||||
|
with_delay=(not test_mode),
|
||||||
|
with_failure=(not test_mode),
|
||||||
|
with_authorization=(not test_mode),
|
||||||
)
|
)
|
||||||
self.files = MockUploader(app)
|
self.files = MockUploader(app)
|
||||||
self.reports = MockReportingProvider()
|
self.reports = MockReportingProvider()
|
||||||
|
@ -215,7 +215,7 @@ class CloudProviderInterface:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def suspend_user(self, auth_credentials: Dict, csp_user_id: str) -> bool:
|
def disable_user(self, auth_credentials: Dict, csp_user_id: str) -> bool:
|
||||||
"""Revoke all privileges for a user. Used to prevent user access while a full
|
"""Revoke all privileges for a user. Used to prevent user access while a full
|
||||||
delete is being processed.
|
delete is being processed.
|
||||||
|
|
||||||
@ -235,25 +235,6 @@ class CloudProviderInterface:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def delete_user(self, auth_credentials: Dict, csp_user_id: str) -> bool:
|
|
||||||
"""Given the csp-internal id for a user, initiate user deletion.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
auth_credentials -- Object containing CSP account credentials
|
|
||||||
csp_user_id -- CSP internal user identifier
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool -- True on success
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
AuthenticationException: Problem with the credentials
|
|
||||||
AuthorizationException: Credentials not authorized for current action(s)
|
|
||||||
ConnectionException: Issue with the CSP API connection
|
|
||||||
UnknownServerException: Unknown issue on the CSP side
|
|
||||||
UserRemovalException: User couldn't be removed
|
|
||||||
"""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def get_calculator_url(self) -> str:
|
def get_calculator_url(self) -> str:
|
||||||
"""Returns the calculator url for the CSP.
|
"""Returns the calculator url for the CSP.
|
||||||
This will likely be a static property elsewhere once a CSP is chosen.
|
This will likely be a static property elsewhere once a CSP is chosen.
|
||||||
@ -281,12 +262,15 @@ class MockCloudProvider(CloudProviderInterface):
|
|||||||
ATAT_ADMIN_CREATE_FAILURE_PCT = 12
|
ATAT_ADMIN_CREATE_FAILURE_PCT = 12
|
||||||
UNAUTHORIZED_RATE = 2
|
UNAUTHORIZED_RATE = 2
|
||||||
|
|
||||||
def __init__(self, config, with_delay=True, with_failure=True):
|
def __init__(
|
||||||
|
self, config, with_delay=True, with_failure=True, with_authorization=True
|
||||||
|
):
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import random
|
import random
|
||||||
|
|
||||||
self._with_delay = with_delay
|
self._with_delay = with_delay
|
||||||
self._with_failure = with_failure
|
self._with_failure = with_failure
|
||||||
|
self._with_authorization = with_authorization
|
||||||
self._sleep = sleep
|
self._sleep = sleep
|
||||||
self._random = random
|
self._random = random
|
||||||
|
|
||||||
@ -356,31 +340,18 @@ class MockCloudProvider(CloudProviderInterface):
|
|||||||
self._maybe_raise(self.UNAUTHORIZED_RATE, self.AUTHORIZATION_EXCEPTION)
|
self._maybe_raise(self.UNAUTHORIZED_RATE, self.AUTHORIZATION_EXCEPTION)
|
||||||
return self._id()
|
return self._id()
|
||||||
|
|
||||||
def suspend_user(self, auth_credentials, csp_user_id):
|
def disable_user(self, auth_credentials, csp_user_id):
|
||||||
self._authorize(auth_credentials)
|
self._authorize(auth_credentials)
|
||||||
self._maybe_raise(self.NETWORK_FAILURE_PCT, self.NETWORK_EXCEPTION)
|
self._maybe_raise(self.NETWORK_FAILURE_PCT, self.NETWORK_EXCEPTION)
|
||||||
self._maybe_raise(self.SERVER_FAILURE_PCT, self.SERVER_EXCEPTION)
|
self._maybe_raise(self.SERVER_FAILURE_PCT, self.SERVER_EXCEPTION)
|
||||||
|
|
||||||
self._maybe_raise(
|
self._maybe_raise(
|
||||||
self.ATAT_ADMIN_CREATE_FAILURE_PCT,
|
self.ATAT_ADMIN_CREATE_FAILURE_PCT,
|
||||||
UserRemovalException(csp_user_id, "Could not suspend user."),
|
UserRemovalException(csp_user_id, "Could not disable user."),
|
||||||
)
|
)
|
||||||
|
|
||||||
return self._maybe(12)
|
return self._maybe(12)
|
||||||
|
|
||||||
def delete_user(self, auth_credentials, csp_user_id):
|
|
||||||
self._authorize(auth_credentials)
|
|
||||||
self._maybe_raise(self.NETWORK_FAILURE_PCT, self.NETWORK_EXCEPTION)
|
|
||||||
self._maybe_raise(self.SERVER_FAILURE_PCT, self.SERVER_EXCEPTION)
|
|
||||||
|
|
||||||
self._maybe_raise(
|
|
||||||
self.ATAT_ADMIN_CREATE_FAILURE_PCT,
|
|
||||||
UserRemovalException(csp_user_id, "Could not delete user."),
|
|
||||||
)
|
|
||||||
|
|
||||||
self._maybe_raise(self.UNAUTHORIZED_RATE, self.AUTHORIZATION_EXCEPTION)
|
|
||||||
return self._maybe(12)
|
|
||||||
|
|
||||||
def get_calculator_url(self):
|
def get_calculator_url(self):
|
||||||
return "https://www.rackspace.com/en-us/calculator"
|
return "https://www.rackspace.com/en-us/calculator"
|
||||||
|
|
||||||
@ -410,5 +381,5 @@ class MockCloudProvider(CloudProviderInterface):
|
|||||||
|
|
||||||
def _authorize(self, credentials):
|
def _authorize(self, credentials):
|
||||||
self._delay(1, 5)
|
self._delay(1, 5)
|
||||||
if credentials != self._auth_credentials:
|
if self._with_authorization and credentials != self._auth_credentials:
|
||||||
raise self.AUTHENTICATION_EXCEPTION
|
raise self.AUTHENTICATION_EXCEPTION
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from sqlalchemy.orm.exc import NoResultFound
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
|
from flask import current_app as app
|
||||||
|
|
||||||
from atst.database import db
|
from atst.database import db
|
||||||
from atst.models import (
|
from atst.models import (
|
||||||
@ -91,3 +92,16 @@ class EnvironmentRoles(object):
|
|||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
return [id_ for id_, in results]
|
return [id_ for id_, in results]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def disable(cls, environment_role_id):
|
||||||
|
environment_role = EnvironmentRoles.get_by_id(environment_role_id)
|
||||||
|
|
||||||
|
credentials = environment_role.environment.csp_credentials
|
||||||
|
app.csp.cloud.disable_user(credentials, environment_role.csp_user_id)
|
||||||
|
|
||||||
|
environment_role.status = EnvironmentRole.Status.DISABLED
|
||||||
|
db.session.add(environment_role)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return environment_role
|
||||||
|
@ -39,7 +39,7 @@ class EnvironmentRole(
|
|||||||
class Status(Enum):
|
class Status(Enum):
|
||||||
PENDING = "pending"
|
PENDING = "pending"
|
||||||
COMPLETED = "completed"
|
COMPLETED = "completed"
|
||||||
PENDING_DELETE = "pending_delete"
|
DISABLED = "disabled"
|
||||||
|
|
||||||
status = Column(SQLAEnum(Status, native_enum=False), default=Status.PENDING)
|
status = Column(SQLAEnum(Status, native_enum=False), default=Status.PENDING)
|
||||||
|
|
||||||
|
@ -5,3 +5,4 @@ PGDATABASE = atat_test
|
|||||||
REDIS_URI = redis://redishost:6379
|
REDIS_URI = redis://redishost:6379
|
||||||
CRL_STORAGE_CONTAINER = tests/fixtures/crl
|
CRL_STORAGE_CONTAINER = tests/fixtures/crl
|
||||||
WTF_CSRF_ENABLED = false
|
WTF_CSRF_ENABLED = false
|
||||||
|
CSP=mock-test
|
||||||
|
@ -36,9 +36,5 @@ def test_create_or_update_user(mock_csp: MockCloudProvider):
|
|||||||
assert isinstance(csp_user_id, str)
|
assert isinstance(csp_user_id, str)
|
||||||
|
|
||||||
|
|
||||||
def test_suspend_user(mock_csp: MockCloudProvider):
|
def test_disable_user(mock_csp: MockCloudProvider):
|
||||||
assert mock_csp.suspend_user(CREDENTIALS, "csp_user_id")
|
assert mock_csp.disable_user(CREDENTIALS, "csp_user_id")
|
||||||
|
|
||||||
|
|
||||||
def test_delete_user(mock_csp: MockCloudProvider):
|
|
||||||
assert mock_csp.delete_user(CREDENTIALS, "csp_user_id")
|
|
||||||
|
@ -76,3 +76,15 @@ def test_get_for_application_member_does_not_return_deleted(
|
|||||||
|
|
||||||
roles = EnvironmentRoles.get_for_application_member(application_role.id)
|
roles = EnvironmentRoles.get_for_application_member(application_role.id)
|
||||||
assert len(roles) == 0
|
assert len(roles) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_disable_completed(application_role, environment):
|
||||||
|
environment_role = EnvironmentRoleFactory.create(
|
||||||
|
application_role=application_role,
|
||||||
|
environment=environment,
|
||||||
|
status=EnvironmentRole.Status.COMPLETED,
|
||||||
|
)
|
||||||
|
|
||||||
|
environment_role = EnvironmentRoles.disable(environment_role.id)
|
||||||
|
|
||||||
|
assert environment_role.status == EnvironmentRole.Status.DISABLED
|
||||||
|
Loading…
x
Reference in New Issue
Block a user