Implement EnvironmentRoles.disable

This commit is contained in:
richard-dds 2019-10-29 15:57:57 -04:00
parent ec44d4a560
commit d1e6533824
7 changed files with 98 additions and 45 deletions

View File

@ -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,
),
)

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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")

View File

@ -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