From bb390d2ba702498c179bc7d6524021fba41a9d70 Mon Sep 17 00:00:00 2001 From: Mike Williamson Date: Mon, 27 Apr 2020 21:26:23 -0400 Subject: [PATCH 1/2] Add users to organisations by default This commit creates an org for each user that is created. This org is the default org to which ad hoc scans will be attached. --- api/models/Users.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/api/models/Users.py b/api/models/Users.py index 8606e087d8..a7f9cd218f 100644 --- a/api/models/Users.py +++ b/api/models/Users.py @@ -3,7 +3,10 @@ from sqlalchemy import Column, String, ForeignKey from sqlalchemy import event from app import bcrypt +from models.User_affiliations import User_affiliations +from models.Organizations import Organizations from sqlalchemy.orm import relationship, validates +from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method from db import Base @@ -23,6 +26,16 @@ class Users(Base): "User_affiliations", back_populates="user", cascade="all, delete" ) + def __init__(self, **kwargs): + super(Users, self).__init__(**kwargs) + # XXX: this breaks most of the tests + self.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations(acronym=self.user_name), + ) + ) + @hybrid_method def find_by_user_name(self, user_name): return self.query.filter(self.user_name == user_name).first() From 5dbbea3b8112af2046af3d222e2ac47bd6d471cb Mon Sep 17 00:00:00 2001 From: Mike Williamson Date: Thu, 7 May 2020 00:33:08 -0400 Subject: [PATCH 2/2] Add users to an org by default This commit started it's life as a relatively small feature adding a user to an org by default to ensure that users has a usable account after logging in, but ended up with some serious mission creep. Writing tests for this feature uncovered a lack of isolation between tests, and tests that mostly failed with `KeyError` or `'NoneType' object is not subscriptable` errors, as well as test data with clashing ids. This change had some far reaching consequences and I basically kept reworking the tests until the isolation errors went away and I could tell if the "user orgs" feature had actually broken stuff. If it did, I fixed that too. --- api/Pipfile | 1 + api/Pipfile.lock | 45 +- api/functions/auth_wrappers.py | 2 + api/functions/sign_in_user.py | 4 +- api/migrations/versions/1860746a39e4_.py | 76 ++ api/migrations/versions/b781fb62a115_.py | 30 + api/models/Organizations.py | 20 +- api/models/User_affiliations.py | 25 +- api/models/Users.py | 16 +- api/models/__init__.py | 4 +- api/schemas/organizations.py | 18 +- api/schemas/organizations_mutations.py | 243 ++-- api/tests/test_auth_functions.py | 1 - api/tests/test_cost_check.py | 9 +- api/tests/test_createOrganization_mutation.py | 308 +++++ api/tests/test_db_migrate.py | 22 - api/tests/test_depth_check.py | 176 +-- api/tests/test_dmarc_reports.py | 20 + api/tests/test_domains_mutations.py | 294 ++-- .../test_domains_resolver_access_control.py | 12 +- api/tests/test_domains_resolver_values.py | 1182 +++++------------ api/tests/test_organization_mutations.py | 1102 --------------- ...st_organization_resolver_access_control.py | 573 +++----- .../test_organization_resolver_values.py | 893 +++++++------ api/tests/test_organizations.py | 17 + api/tests/test_removeOrganization_mutation.py | 290 ++++ api/tests/test_updateOrganization_mutation.py | 338 +++++ api/tests/test_user_access_control.py | 25 +- api/tests/test_user_mutations.py | 9 +- api/tests/test_user_values.py | 80 +- api/tests/test_users.py | 20 +- api/tests/test_users_access_control.py | 86 +- api/tests/testdata/__init__.py | 56 + 33 files changed, 2702 insertions(+), 3295 deletions(-) create mode 100644 api/migrations/versions/1860746a39e4_.py create mode 100644 api/migrations/versions/b781fb62a115_.py create mode 100644 api/tests/test_createOrganization_mutation.py delete mode 100644 api/tests/test_db_migrate.py create mode 100644 api/tests/test_dmarc_reports.py delete mode 100644 api/tests/test_organization_mutations.py create mode 100644 api/tests/test_organizations.py create mode 100644 api/tests/test_removeOrganization_mutation.py create mode 100644 api/tests/test_updateOrganization_mutation.py create mode 100644 api/tests/testdata/__init__.py diff --git a/api/Pipfile b/api/Pipfile index 106066ecd9..eee0d82f59 100644 --- a/api/Pipfile +++ b/api/Pipfile @@ -29,6 +29,7 @@ flask-migrate = "*" pytest = "*" pyotp = "*" pyjwt = "*" +python-slugify = "*" [scripts] test = "python -m pytest" diff --git a/api/Pipfile.lock b/api/Pipfile.lock index a3b216ee76..a4ee2d0a36 100644 --- a/api/Pipfile.lock +++ b/api/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b7cfc60c14492f7d38a230f76839f44c0dc534e5a72feba8de7a51a92be4ce07" + "sha256": "466060c557e96174eeb5361e5953eae7ed6188e509a40e9888954b1b29da5191" }, "pipfile-spec": 6, "requires": { @@ -108,10 +108,10 @@ }, "click": { "hashes": [ - "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc", - "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a" + "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", + "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" ], - "version": "==7.1.1" + "version": "==7.1.2" }, "docopt": { "hashes": [ @@ -424,6 +424,13 @@ ], "version": "==1.0.4" }, + "python-slugify": { + "hashes": [ + "sha256:a8fc3433821140e8f409a9831d13ae5deccd0b033d4744d94b31fea141bdd84c" + ], + "index": "pypi", + "version": "==4.0.0" + }, "requests": { "hashes": [ "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", @@ -477,6 +484,13 @@ "index": "pypi", "version": "==1.3.16" }, + "text-unidecode": { + "hashes": [ + "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", + "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" + ], + "version": "==1.3" + }, "urllib3": { "hashes": [ "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", @@ -510,10 +524,10 @@ "develop": { "astroid": { "hashes": [ - "sha256:71ea07f44df9568a75d0f354c49143a4575d90645e9fead6dfb52c26a85ed13a", - "sha256:840947ebfa8b58f318d42301cf8c0a20fd794a33b61cc4638e28e9e61ba32f42" + "sha256:4c17cea3e592c21b6e222f673868961bad77e1f985cb1694ed077475a89229c1", + "sha256:d8506842a3faf734b81599c8b98dcc423de863adcc1999248480b18bd31a0f38" ], - "version": "==2.3.3" + "version": "==2.4.1" }, "attrs": { "hashes": [ @@ -655,11 +669,11 @@ }, "pylint": { "hashes": [ - "sha256:3db5468ad013380e987410a8d6956226963aed94ecb5f9d3a28acca6d9ac36cd", - "sha256:886e6afc935ea2590b462664b161ca9a5e40168ea99e5300935f6591ad467df4" + "sha256:b95e31850f3af163c2283ed40432f053acbc8fc6eba6a069cb518d9dbf71848c", + "sha256:dd506acce0427e9e08fb87274bcaa953d38b50a58207170dbf5b36cf3e16957b" ], "index": "pypi", - "version": "==2.4.4" + "version": "==2.5.2" }, "pyparsing": { "hashes": [ @@ -691,6 +705,13 @@ ], "version": "==1.14.0" }, + "toml": { + "hashes": [ + "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", + "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" + ], + "version": "==0.10.0" + }, "typed-ast": { "hashes": [ "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355", @@ -734,9 +755,9 @@ }, "wrapt": { "hashes": [ - "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1" + "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7" ], - "version": "==1.11.2" + "version": "==1.12.1" } } } diff --git a/api/functions/auth_wrappers.py b/api/functions/auth_wrappers.py index ad371f05bc..778fe01b78 100644 --- a/api/functions/auth_wrappers.py +++ b/api/functions/auth_wrappers.py @@ -46,6 +46,8 @@ def check_user_claims(user_claims): elif user_claims: user_id = user_claims[0]['user_id'] with app.app_context(): + # XXX: affiliations should have been eager loaded with joinedload + # when user was initally pulled from the db. user_aff = User_affiliations.query.filter( User_affiliations.user_id == user_id).all() user_aff = orm_to_dict(user_aff) diff --git a/api/functions/sign_in_user.py b/api/functions/sign_in_user.py index c160311827..ff959a8c85 100644 --- a/api/functions/sign_in_user.py +++ b/api/functions/sign_in_user.py @@ -56,11 +56,13 @@ def sign_in_user(user_name, password): } user_roles.append(temp_dict) else: + # XXX: Roles is [] || [""] || [{}]? + # why not just []? user_roles = ["none"] try: payload = { "exp": datetime.datetime.utcnow() - + datetime.timedelta(days=0, seconds=1800), + + datetime.timedelta(days=0, seconds=1800), # XXX: too short "iat": datetime.datetime.utcnow(), "user_id": user.id, "roles": user_roles, diff --git a/api/migrations/versions/1860746a39e4_.py b/api/migrations/versions/1860746a39e4_.py new file mode 100644 index 0000000000..5ffc9eaef5 --- /dev/null +++ b/api/migrations/versions/1860746a39e4_.py @@ -0,0 +1,76 @@ +"""empty message + +Revision ID: b781fb62a115 +Revises: 1860746a39e4 +Create Date: 2020-05-06 13:19:46.906351 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "b781fb62a115" +down_revision = "1860746a39e4" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column("organizations", sa.Column("name", sa.String(), nullable=True)) + op.add_column("organizations", sa.Column("slug", sa.String(), nullable=True)) + op.create_index( + op.f("ix_organizations_slug"), "organizations", ["slug"], unique=False + ) + op.drop_constraint("user_user_aff_key", "user_affiliations", type_="foreignkey") + op.drop_constraint( + "user_affiliations_organization_id_fkey", + "user_affiliations", + type_="foreignkey", + ) + op.create_foreign_key( + "user_affiliations_organization_id_fkey", + "user_affiliations", + "organizations", + ["organization_id"], + ["id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) + op.create_foreign_key( + "user_affiliations_users_id_fkey", + "user_affiliations", + "users", + ["user_id"], + ["id"], + onupdate="CASCADE", + ondelete="CASCADE", + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint( + "user_affiliations_users_id_fkey", "user_affiliations", type_="foreignkey" + ) + op.drop_constraint( + "user_affiliations_organization_id_fkey", + "user_affiliations", + type_="foreignkey", + ) + op.create_foreign_key( + "user_affiliations_organization_id_fkey", + "user_affiliations", + "organizations", + ["organization_id"], + ["id"], + ) + op.create_foreign_key( + "user_user_aff_key", "user_affiliations", "users", ["user_id"], ["id"] + ) + op.drop_index(op.f("ix_organizations_slug"), table_name="organizations") + op.drop_column("organizations", "slug") + op.drop_column("organizations", "name") + # ### end Alembic commands ### diff --git a/api/migrations/versions/b781fb62a115_.py b/api/migrations/versions/b781fb62a115_.py new file mode 100644 index 0000000000..871b25c83b --- /dev/null +++ b/api/migrations/versions/b781fb62a115_.py @@ -0,0 +1,30 @@ +"""empty message + +Revision ID: 1860746a39e4 +Revises: cf42b66dbc12 +Create Date: 2020-04-30 22:30:22.681303 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.schema import Sequence, CreateSequence + + +# revision identifiers, used by Alembic. +revision = "1860746a39e4" +down_revision = "cf42b66dbc12" +branch_labels = None +depends_on = None + + +def upgrade(): + op.execute(CreateSequence(Sequence("dmarc_reports_id_seq"))) + op.execute( + "ALTER TABLE dmarc_reports ALTER COLUMN id SET DEFAULT nextval('public.dmarc_reports_id_seq')" + ) + op.execute("ALTER SEQUENCE dmarc_reports_id_seq OWNED BY dmarc_reports.id") + + +def downgrade(): + op.execute("ALTER TABLE dmarc_reports ALTER COLUMN id DROP DEFAULT") + op.execute("DROP SEQUENCE dmarc_reports_id_seq") diff --git a/api/models/Organizations.py b/api/models/Organizations.py index 93272c94ef..784cb72c6e 100644 --- a/api/models/Organizations.py +++ b/api/models/Organizations.py @@ -1,8 +1,8 @@ -from graphene import Time -from sqlalchemy.types import Integer, Boolean, DateTime, Float -from sqlalchemy import Column, String, ForeignKey +from sqlalchemy.types import Integer +from sqlalchemy import Column, String from sqlalchemy.orm import relationship from sqlalchemy.dialects.postgresql import JSONB +from slugify import slugify from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method from db import Base @@ -11,11 +11,17 @@ class Organizations(Base): __tablename__ = "organizations" id = Column(Integer, primary_key=True) + name = Column(String) + slug = Column(String, index=True) acronym = Column(String) org_tags = Column(JSONB) - domains = relationship( - "Domains", back_populates="organization", cascade="all, delete" - ) + domains = relationship("Domains", back_populates="organization", cascade="all, delete") users = relationship( - "User_affiliations", back_populates="user_organization", cascade="all, delete" + "User_affiliations", back_populates="user_organization", passive_deletes=True ) + + def __init__(self, **kwargs): + super(Organizations, self).__init__(**kwargs) + self.slug = slugify(kwargs.get("name", "")) + if self.org_tags is None: + self.org_tags = dict() diff --git a/api/models/User_affiliations.py b/api/models/User_affiliations.py index b0d22d73c9..9494479d4f 100644 --- a/api/models/User_affiliations.py +++ b/api/models/User_affiliations.py @@ -8,12 +8,31 @@ class User_affiliations(Base): __tablename__ = "user_affiliations" id = Column(Integer, primary_key=True, autoincrement=True) - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - organization_id = Column(Integer, ForeignKey("organizations.id")) + user_id = Column( + Integer, + ForeignKey( + "users.id", + onupdate="CASCADE", + ondelete="CASCADE", + # name is required on FK or rollback is broken + name="user_affiliations_users_id_fkey", + ), + primary_key=True, + ) + organization_id = Column( + Integer, + ForeignKey( + "organizations.id", + onupdate="CASCADE", + ondelete="CASCADE", + # name is required on FK or rollback is broken + name="user_affiliations_organization_id_fkey", + ), + ) permission = Column(String) user = relationship( "Users", back_populates="user_affiliation", cascade="all, delete" ) user_organization = relationship( - "Organizations", back_populates="users", cascade="all, delete" + "Organizations", back_populates="users", cascade="all, delete", ) diff --git a/api/models/Users.py b/api/models/Users.py index a7f9cd218f..3c360e054f 100644 --- a/api/models/Users.py +++ b/api/models/Users.py @@ -1,10 +1,11 @@ from sqlalchemy.types import Integer, Boolean, Float +from slugify import slugify from functions.orm_to_dict import orm_to_dict from sqlalchemy import Column, String, ForeignKey from sqlalchemy import event from app import bcrypt -from models.User_affiliations import User_affiliations from models.Organizations import Organizations +from models.User_affiliations import User_affiliations from sqlalchemy.orm import relationship, validates from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method @@ -23,16 +24,21 @@ class Users(Base): failed_login_attempt_time = Column(Float, default=0, nullable=True) tfa_validated = Column(Boolean, default=False) user_affiliation = relationship( - "User_affiliations", back_populates="user", cascade="all, delete" + "User_affiliations", back_populates="user", passive_deletes=True, ) def __init__(self, **kwargs): super(Users, self).__init__(**kwargs) - # XXX: this breaks most of the tests + # XXX: This is gross but matches the expections of the + # Acronym scalar type. + acronym = slugify(self.user_name).upper()[:10] self.user_affiliation.append( User_affiliations( - permission="super_admin", - user_organization=Organizations(acronym=self.user_name), + permission="admin", + user_organization=Organizations( + name=self.user_name, + acronym=acronym, + ) ) ) diff --git a/api/models/__init__.py b/api/models/__init__.py index 3db59e5fc2..7da071b2f3 100644 --- a/api/models/__init__.py +++ b/api/models/__init__.py @@ -12,7 +12,7 @@ class Domains(Base): __tablename__ = "domains" - id = Column(Integer, primary_key=True) + id = Column(Integer, primary_key=True, autoincrement=True) domain = Column(String) last_run = Column(DateTime) organization_id = Column(Integer, ForeignKey("organizations.id")) @@ -41,7 +41,7 @@ class Dmarc_Reports(Base): class Scans(Base): __tablename__ = "scans" - id = Column(Integer, primary_key=True) + id = Column(Integer, primary_key=True, autoincrement=True) domain_id = Column(Integer, ForeignKey("domains.id")) scan_date = Column(DateTime) initiated_by = Column(Integer, ForeignKey("users.id")) diff --git a/api/schemas/organizations.py b/api/schemas/organizations.py index 62dda48aca..4d271fb174 100644 --- a/api/schemas/organizations.py +++ b/api/schemas/organizations.py @@ -30,12 +30,12 @@ class Meta: "domains", "users" ) + name = graphene.String( + description="The full name of the organization." + ) acronym = Acronym( description="The acronym of the organization." ) - description = graphene.String( - description="The full name of the organization." - ) zone = graphene.String( description="The zone which the organization belongs to." ) @@ -61,20 +61,20 @@ class Meta: def resolve_acronym(self: OrgModel, info): return self.acronym - def resolve_description(self: OrgModel, info): - return self.org_tags["description"] + def resolve_name(self: OrgModel, info): + return self.name def resolve_zone(self: OrgModel, info): - return self.org_tags["zone"] + return self.org_tags.get("zone", None) def resolve_sector(self: OrgModel, info): - return self.org_tags["sector"] + return self.org_tags.get("sector", None) def resolve_province(self: OrgModel, info): - return self.org_tags["province"] + return self.org_tags.get("province", None) def resolve_city(self: OrgModel, info): - return self.org_tags["city"] + return self.org_tags.get("city", None) def resolve_domains(self: OrgModel, info): query = DomainsSchema.get_query(info) diff --git a/api/schemas/organizations_mutations.py b/api/schemas/organizations_mutations.py index ad256e2688..c3556a5819 100644 --- a/api/schemas/organizations_mutations.py +++ b/api/schemas/organizations_mutations.py @@ -1,14 +1,10 @@ import graphene - from graphql import GraphQLError - from app import app from db import db_session - from functions.auth_wrappers import require_token from functions.auth_functions import is_super_admin from functions.input_validators import cleanse_input - from models import ( Organizations, User_affiliations, @@ -20,7 +16,6 @@ Dkim_scans, Dmarc_scans, Mx_scans, - ) from scalars.organization_acronym import Acronym @@ -30,69 +25,61 @@ class CreateOrganization(graphene.Mutation): """ Mutation allows the creation of an organization inside the database. """ + class Arguments: - acronym = Acronym( - description="Acronym of organization.", - required=True - ) - description = graphene.String( - description="Full name of organization.", - required=True - ) + acronym = Acronym(description="Acronym of organization.", required=True) + name = graphene.String(description="Full name of organization.", required=True) zone = graphene.String( - description="The zone which the organization belongs to.", - required=True + description="The zone which the organization belongs to.", required=True ) sector = graphene.String( - description="The sector which the organization belongs to.", - required=True + description="The sector which the organization belongs to.", required=True ) province = graphene.String( description="The province in which the organization is located in.", - required=True + required=True, ) city = graphene.String( description="The city in which the organization is located in.", - required=True + required=True, ) # If the update passed or failed status = graphene.Boolean() with app.app_context(): + @require_token def mutate(self, info, **kwargs): - user_roles = kwargs.get('user_roles') - acronym = cleanse_input(kwargs.get('acronym')) - description = cleanse_input(kwargs.get('description')) - zone = cleanse_input(kwargs.get('zone')) - sector = cleanse_input(kwargs.get('sector')) - province = cleanse_input(kwargs.get('province')) - city = cleanse_input(kwargs.get('city')) + user_roles = kwargs.get("user_roles") + name = cleanse_input(kwargs.get("name")) + acronym = cleanse_input(kwargs.get("acronym")) + zone = cleanse_input(kwargs.get("zone")) + sector = cleanse_input(kwargs.get("sector")) + province = cleanse_input(kwargs.get("province")) + city = cleanse_input(kwargs.get("city")) if is_super_admin(user_role=user_roles): # Check to see if organization already exists - org_orm = db_session.query(Organizations).filter( - Organizations.acronym == acronym - ).first() + org_orm = ( + db_session.query(Organizations) + .filter(Organizations.acronym == acronym) + .first() + ) if org_orm is not None: - raise GraphQLError("Error, Organization alredy exists") + raise GraphQLError("Error, Organization already exists") # Generate org tags org_tags = { - "description": description, "zone": zone, "sector": sector, "province": province, - "city": city + "city": city, } # Create new org entry in db - new_org = Organizations( - acronym=acronym, - org_tags=org_tags - ) + new_org = Organizations(name=name, acronym=acronym, org_tags=org_tags) # Add new org entry into the session db_session.add(new_org) @@ -114,89 +101,82 @@ def mutate(self, info, **kwargs): class UpdateOrganization(graphene.Mutation): class Arguments: acronym = Acronym( - description="Organization that will be updated.", - required=True + description="Organization that will be updated.", required=True ) updated_acronym = Acronym( - description="Organization Acronym you would like updated", - required=False - ) - description = graphene.String( - description="Full name of organization.", - required=False + description="Organization Acronym you would like updated", required=False ) + name = graphene.String(description="Full name of organization.", required=False) zone = graphene.String( - description="The zone which the organization belongs to.", - required=False + description="The zone which the organization belongs to.", required=False ) sector = graphene.String( - description="The sector which the organization belongs to.", - required=False + description="The sector which the organization belongs to.", required=False ) province = graphene.String( description="The province in which the organization is located in.", - required=False + required=False, ) city = graphene.String( description="The city in which the organization is located in.", - required=False + required=False, ) # If the update passed or failed status = graphene.Boolean() with app.app_context(): + @require_token def mutate(self, info, **kwargs): # Get arguments from mutation - user_roles = kwargs.get('user_roles') - acronym = cleanse_input(kwargs.get('acronym')) - updated_acronym = cleanse_input(kwargs.get('updated_acronym')) - description = cleanse_input(kwargs.get('description')) - zone = cleanse_input(kwargs.get('zone')) - sector = cleanse_input(kwargs.get('sector')) - province = cleanse_input(kwargs.get('province')) - city = cleanse_input(kwargs.get('city')) - + user_roles = kwargs.get("user_roles") + name = cleanse_input(kwargs.get("name")) + acronym = cleanse_input(kwargs.get("acronym")) + updated_acronym = cleanse_input(kwargs.get("updated_acronym")) + description = cleanse_input(kwargs.get("description")) + zone = cleanse_input(kwargs.get("zone")) + sector = cleanse_input(kwargs.get("sector")) + province = cleanse_input(kwargs.get("province")) + city = cleanse_input(kwargs.get("city")) + + # XXX: only the Super User can edit orgs? if is_super_admin(user_role=user_roles): # Get requested org orm - org_orm = db_session.query(Organizations).filter( - Organizations.acronym == acronym - ).first() + org_orm = ( + db_session.query(Organizations) + .filter(Organizations.acronym == acronym) + .first() + ) # Check to see if org exists if org_orm is None: raise GraphQLError("Error, organization does not exist.") # Check to see if organization acronym already in use - update_org_orm = db_session.query(Organizations).filter( - Organizations.acronym == updated_acronym - ).first() + update_org_orm = ( + db_session.query(Organizations) + .filter(Organizations.acronym == updated_acronym) + .first() + ) if update_org_orm is not None: raise GraphQLError("Error, acronym already in use.") - # Generate org tags - org_tags = { - "description": description, - "zone": zone, - "sector": sector, - "province": province, - "city": city - } if updated_acronym is not acronym: # Update orm - Organizations.query.filter( - Organizations.acronym == acronym - ).update( - { - 'acronym': updated_acronym, - 'org_tags': org_tags - } - ) + org_orm.name = name + org_orm.acronym = updated_acronym + org_orm.org_tags = { + "zone": zone, + "sector": sector, + "province": province, + "city": city, + } # Push update to db and return status + try: db_session.commit() return UpdateOrganization(status=True) @@ -214,87 +194,83 @@ class RemoveOrganization(graphene.Mutation): """ Mutation allows the removal of an organization inside the database. """ + class Arguments: acronym = Acronym( - description="The organization you wish to remove", - required=True + description="The organization you wish to remove", required=True ) status = graphene.Boolean() with app.app_context(): + @require_token def mutate(self, info, **kwargs): # Get arguments from mutation - user_roles = kwargs.get('user_roles') - acronym = cleanse_input(kwargs.get('acronym')) + user_roles = kwargs.get("user_roles") + acronym = cleanse_input(kwargs.get("acronym")) # Restrict the deletion of SA Org if acronym == "SA": raise GraphQLError("Error, you cannot remove this organization") # Check to see if org exists - org_orm = db_session.query(Organizations).filter( - Organizations.acronym == acronym - ).first() + org_orm = ( + db_session.query(Organizations) + .filter(Organizations.acronym == acronym) + .first() + ) if org_orm is None: raise GraphQLError("Error, organization does not exist") # Check Permissions if is_super_admin(user_role=user_roles): + # XXX shouldn't cascade delete do all of this for us? try: - # Get Org ID - org_orm = Organizations.query.filter( - Organizations.acronym == acronym - ).first() - # Get All Domains domain_orm = Domains.query.filter( Domains.organization_id == org_orm.id ).all() - # Loop Through All Domains - for domain in domain_orm: - # Get All Scans - scan_orm = Scans.query.filter( - Scans.domain_id == domain.id - ).all() - # Delete All Related Scans - for scan in scan_orm: + if len(domain_orm) > 0: + # Loop Through All Domains + for domain in domain_orm: + # Get All Scans + scan_orm = Scans.query.filter( + Scans.domain_id == domain.id + ).all() + # Delete All Related Scans + for scan in scan_orm: + try: + Dkim_scans.query.filter( + Dkim_scans.id == scan.id + ).delete() + Dmarc_scans.query.filter( + Dmarc_scans.id == scan.id + ).delete() + Https_scans.query.filter( + Https_scans.id == scan.id + ).delete() + Mx_scans.query.filter( + Mx_scans.id == scan.id + ).delete() + Spf_scans.query.filter( + Spf_scans.id == scan.id + ).delete() + Ssl_scans.query.filter( + Ssl_scans.id == scan.id + ).delete() + Scans.query.filter(Scans.id == scan.id).delete() + except Exception as e: + print("Scans: " + e) + return RemoveOrganization(status=False) + # Delete Domains try: - Dkim_scans.query.filter( - Dkim_scans.id == scan.id - ).delete() - Dmarc_scans.query.filter( - Dmarc_scans.id == scan.id - ).delete() - Https_scans.query.filter( - Https_scans.id == scan.id - ).delete() - Mx_scans.query.filter( - Mx_scans.id == scan.id - ).delete() - Spf_scans.query.filter( - Spf_scans.id == scan.id - ).delete() - Ssl_scans.query.filter( - Ssl_scans.id == scan.id - ).delete() - Scans.query.filter( - Scans.id == scan.id - ).delete() + Domains.query.filter(Domains.id == domain.id).delete() except Exception as e: - print("Scans: " + e) + print("Domain: " + str(e)) return RemoveOrganization(status=False) - # Delete Domains - try: - Domains.query.filter( - Domains.id == domain.id - ).delete() - except Exception as e: - print("Domain: " + str(e)) - return RemoveOrganization(status=False) try: # Get all user aff @@ -305,11 +281,10 @@ def mutate(self, info, **kwargs): print("user_aff: " + str(e)) return RemoveOrganization(status=False) - Organizations.query.filter( - Organizations.acronym == acronym - ).delete() + db_session.delete(org_orm) db_session.commit() return RemoveOrganization(status=True) + except Exception as e: print("organization: " + str(e)) db_session.rollback() diff --git a/api/tests/test_auth_functions.py b/api/tests/test_auth_functions.py index f3bd729334..c5f6a61ecd 100644 --- a/api/tests/test_auth_functions.py +++ b/api/tests/test_auth_functions.py @@ -1,6 +1,5 @@ import pytest from app import app -from db import db_session from models import Users, User_affiliations, Organizations from functions.auth_functions import ( is_super_admin, diff --git a/api/tests/test_cost_check.py b/api/tests/test_cost_check.py index 2f042a7d36..6cb68c7d1d 100644 --- a/api/tests/test_cost_check.py +++ b/api/tests/test_cost_check.py @@ -3,12 +3,13 @@ from werkzeug.test import create_environ from flask import Request from unittest import TestCase -from db import db_session +from db import DB from app import app from queries import schema from models import Users from backend.security_check import SecurityAnalysisBackend +_, cleanup, db_session = DB() @pytest.fixture(scope="class") def user_schema_test_db_init(): @@ -22,11 +23,9 @@ def user_schema_test_db_init(): db_session.add(test_super_admin) db_session.commit() - yield + yield + cleanup() - with app.app_context(): - Users.query.delete() - db_session.commit() ## diff --git a/api/tests/test_createOrganization_mutation.py b/api/tests/test_createOrganization_mutation.py new file mode 100644 index 0000000000..caedb6c104 --- /dev/null +++ b/api/tests/test_createOrganization_mutation.py @@ -0,0 +1,308 @@ +import pytest +from json_web_token import tokenize, auth_header +from flask import Request +from pytest import fail +from graphene.test import Client +from unittest import TestCase +from werkzeug.test import create_environ +from app import app +from db import DB +from queries import schema +from backend.security_check import SecurityAnalysisBackend +from models import ( + Organizations, + Domains, + Users, + User_affiliations, + Scans, + Dkim_scans, + Dmarc_scans, + Https_scans, + Mx_scans, + Spf_scans, + Ssl_scans, +) + +s, cleanup, session = DB() + + +@pytest.fixture +def save(): + with app.app_context(): + yield s + cleanup() + + +def test_mutation_createOrganization_fails_for_existing_orgs(save): + """ + Test To See If SA Can Create Organization + """ + sa_user = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + ) + sa_user.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations( + acronym="SA", org_tags={"name": "SA"}, name="Super Admin" + ), + ) + ) + + save(sa_user) + + token = tokenize(user_id=sa_user.id, roles=sa_user.roles) + + result = Client(schema).execute( + """ + mutation { + created_org:createOrganization( + name: "Super Admin" + acronym: "SA" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected createOrganization to fail for an existing org. Instead: {}".format( + result + ) + ) + + +def test_mutation_createOrganization_as_super_user(save): + """ + Test To See If SA Can Create Organization + """ + sa_user = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + ) + sa_user.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations( + acronym="SA", org_tags={"name": "SA"}, name="Super Admin" + ), + ) + ) + + save(sa_user) + + token = tokenize(user_id=sa_user.id, roles=sa_user.roles) + + result = Client(schema).execute( + """ + mutation { + created_org:createOrganization( + name: "Test Organization" + acronym: "TEST_ORG" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" in result: + fail( + "expected createOrganization to succeed for super admin. Instead: {}".format( + result + ) + ) + + created_org = result["data"].values() + [status] = created_org + + assert status == {"status": True} + + +def test_mutation_createOrganization_fails_for_write_users(save): + write_user = Users( + display_name="writer", + user_name="writer@example.com", + password="testpassword123", + ) + + write_user.user_affiliation.append( + User_affiliations( + permission="user_write", + user_organization=Organizations(acronym="ORG1", name="Org One"), + ) + ) + + save(write_user) + + token = tokenize(user_id=write_user.id, roles=write_user.roles) + + result = Client(schema).execute( + """ + mutation { + createOrganization( + acronym: "USER_W_NEW" + name: "Test Organization" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected createOrganization to fail for user_write users. Instead: {}".format( + result + ) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, you do not have permission to create organizations" + + +def test_mutation_createOrganization_fails_for_admin_users(save): + admin = Users( + display_name="admin", user_name="admin@example.com", password="testpassword123", + ) + + save(admin) + + token = tokenize(user_id=admin.id, roles=admin.roles) + + result = Client(schema).execute( + """ + mutation { + createOrganization( + name: "New thing" + acronym: "NEW" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected createOrganization to fail for admins. Instead: {}".format(result) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, you do not have permission to create organizations" + + +def test_mutation_createOrganization_fails_for_read_users(save): + reader = Users( + display_name="reader", + user_name="reader@example.com", + password="testpassword123", + ) + + reader.user_affiliation.append( + User_affiliations( + permission="user_read", + user_organization=Organizations(acronym="ORG1", name="Org One"), + ) + ) + + save(reader) + + token = tokenize(user_id=reader.id, roles=reader.roles) + + result = Client(schema).execute( + """ + mutation { + createOrganization( + acronym: "USER_R_NEW" + name: "Test Organization" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected createOrganization to fail for read users. Instead: {}".format( + result + ) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, you do not have permission to create organizations" + + +def test_mutation_createOrganization_fails_for_admin_users(save): + admin = Users( + display_name="admin", user_name="admin@example.com", password="testpassword123", + ) + + save(admin) + + token = tokenize(user_id=admin.id, roles=admin.roles) + + result = Client(schema).execute( + """ + mutation { + createOrganization( + name: "New thing" + acronym: "NEW" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected createOrganization to fail for admins. Instead: {}".format(result) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, you do not have permission to create organizations" diff --git a/api/tests/test_db_migrate.py b/api/tests/test_db_migrate.py deleted file mode 100644 index 152c746f81..0000000000 --- a/api/tests/test_db_migrate.py +++ /dev/null @@ -1,22 +0,0 @@ -import sys -import os -from os.path import dirname, join, expanduser, normpath, realpath -import pytest -from sqlalchemy import create_engine -from manage import * -from db import db_session, Base, engine - -class TestDBCreation: - def test_created_schema_contains_expected_tables(self): - """This test determines that the postgres db is created with the expected tables""" - # Arrange - tables = set() - - # Act - tables.add("alembic_version") - - for key in Base.metadata.tables.keys(): - tables.add(key) - - # Assert - assert tables == set(engine.table_names()) diff --git a/api/tests/test_depth_check.py b/api/tests/test_depth_check.py index e6184763e0..8daca23bb2 100644 --- a/api/tests/test_depth_check.py +++ b/api/tests/test_depth_check.py @@ -2,102 +2,100 @@ from flask import Request import pytest from graphene.test import Client -from unittest import TestCase from app import app -from db import db_session +from db import DB from queries import schema from models import Users from backend.security_check import SecurityAnalysisBackend -@pytest.fixture(scope="class") -def user_schema_test_db_init(): +@pytest.fixture +def save(): with app.app_context(): - test_super_admin = Users( - id=2, - display_name="testsuperadmin", - user_name="testsuperadmin@testemail.ca", - password="testpassword123", - ) - db_session.add(test_super_admin) - db_session.commit() + save, cleanup, _ = DB() + yield save + cleanup() - yield - with app.app_context(): - Users.query.delete() - db_session.commit() +def test_valid_depth_query(save): + test_super_admin = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + ) + save(test_super_admin) + backend = SecurityAnalysisBackend() + client = Client(schema) + get_token = client.execute( + """ + mutation{ + signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ + authToken + } + } + """, + backend=backend, + ) -## -# This class of tests works within the 'createUser' api endpoint -@pytest.mark.usefixtures("user_schema_test_db_init") -class TestDepthCheck(TestCase): - def test_valid_depth_query(self): - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None + assert get_token["data"]["signIn"]["authToken"] is not None + token = get_token["data"]["signIn"]["authToken"] + assert token is not None - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) + environ = create_environ() + environ.update(HTTP_AUTHORIZATION=token) + request_headers = Request(environ) - query = client.execute( - """ - { - user { - displayName - } - } - """, - context_value=request_headers, - backend=backend, - ) - result_refr = {"data": {"user": [{"displayName": "testsuperadmin"}]}} - self.assertDictEqual(result_refr, query) + results = client.execute( + """ + { + user { + displayName + } + } + """, + context_value=request_headers, + backend=backend, + ) + user = results["data"].values() + [[details]] = user + assert details == {"displayName": "testsuperadmin"} + + +def test_invalid_depth_query(save): + test_super_admin = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + ) + save(test_super_admin) - def test_invalid_depth_query(self): - backend = SecurityAnalysisBackend(10) - client = Client(schema) - executed = client.execute( - """ - { - getSectorById(id: 1) { - groups{ - edges{ - node{ - groupSector{ - groups{ - edges{ - node{ - groupSector{ - groups{ - edges{ - node{ - groupSector{ - groups{ - edges{ - node{ - groupSector{ - groups{ - edges{ - node{ - groupSector - } - } + backend = SecurityAnalysisBackend(10) + client = Client(schema) + executed = client.execute( + """ + { + getSectorById(id: 1) { + groups{ + edges{ + node{ + groupSector{ + groups{ + edges{ + node{ + groupSector{ + groups{ + edges{ + node{ + groupSector{ + groups{ + edges{ + node{ + groupSector{ + groups{ + edges{ + node{ + groupSector } } } @@ -117,9 +115,11 @@ def test_invalid_depth_query(self): } } } - """, - backend=backend, - ) - assert executed["errors"] - assert executed["errors"][0] - assert executed["errors"][0]["message"] == "Query is too complex" + } + } + """, + backend=backend, + ) + assert executed["errors"] + assert executed["errors"][0] + assert executed["errors"][0]["message"] == "Query is too complex" diff --git a/api/tests/test_dmarc_reports.py b/api/tests/test_dmarc_reports.py new file mode 100644 index 0000000000..39fd898968 --- /dev/null +++ b/api/tests/test_dmarc_reports.py @@ -0,0 +1,20 @@ +from pytest import fail +from app import app +from sqlalchemy import exc +from db import db_session +from models import ( + Dmarc_Reports, +) + +def test_should_save_without_raising_an_error(): + with app.app_context(): + try: + report = Dmarc_Reports( + start_date="2018-10-01 13:07:12", + end_date="2018-10-01 13:07:12", + report={}, + ) + db_session.add(report) + db_session.commit() + except exc.IntegrityError: + fail("Saving Dmarc_Reports model instance failed") diff --git a/api/tests/test_domains_mutations.py b/api/tests/test_domains_mutations.py index 3873eb2d7d..2ad1b27546 100644 --- a/api/tests/test_domains_mutations.py +++ b/api/tests/test_domains_mutations.py @@ -6,7 +6,7 @@ from app import app from queries import schema from backend.security_check import SecurityAnalysisBackend -from db import db_session +from db import DB from models import ( Organizations, Domains, @@ -21,206 +21,194 @@ Ssl_scans, ) +save, cleanup, session = DB() @pytest.fixture(scope="class") def domain_test_db_init(): with app.app_context(): + org1 = Organizations(acronym="ORG1") + org2 = Organizations(acronym="ORG2") + save(org1) + save(org2) + test_read = Users( - id=1, display_name="testuserread", user_name="testuserread@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations( + user_organization=org1, permission="user_read" + ) + ] ) - db_session.add(test_read) + save(test_read) test_super_admin = Users( - id=2, display_name="testsuperadmin", user_name="testsuperadmin@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations( + user_organization=org2, permission="super_admin" + ) + ] ) - db_session.add(test_super_admin) + save(test_super_admin) test_admin = Users( - id=3, display_name="testadmin", user_name="testadmin@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations( + user_organization=org1, permission="admin" + ) + ] ) - db_session.add(test_admin) - test_admin = Users( - id=4, + save(test_admin) + test_admin2 = Users( display_name="testadmin2", user_name="testadmin2@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations( + user_organization=org2, permission="admin" + ) + ] ) - db_session.add(test_admin) + save(test_admin2) test_write = Users( - id=5, display_name="testuserwrite", user_name="testuserwrite@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations( + user_organization=org1, permission="user_write" + ) + ] ) - db_session.add(test_write) + save(test_write) test_write2 = Users( - id=6, display_name="testuserwrite2", user_name="testuserwrite2@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations( + user_organization=org2, permission="user_write" + ) + ] ) - db_session.add(test_write2) + save(test_write2) - org = Organizations(id=1, acronym="ORG1") - db_session.add(org) - org = Organizations(id=2, acronym="ORG2") - db_session.add(org) - test_user_read_role = User_affiliations( - user_id=1, organization_id=1, permission="user_read" - ) - db_session.add(test_user_read_role) - test_super_admin_role = User_affiliations( - user_id=2, organization_id=2, permission="super_admin" - ) - db_session.add(test_super_admin_role) - test_admin_role = User_affiliations( - user_id=3, organization_id=1, permission="admin" - ) - db_session.add(test_admin_role) - test_admin_role = User_affiliations( - user_id=4, organization_id=2, permission="admin" - ) - db_session.add(test_admin_role) - test_user_write_role = User_affiliations( - user_id=5, organization_id=1, permission="user_write" - ) - db_session.add(test_user_write_role) - test_user_write_role_2 = User_affiliations( - user_id=6, organization_id=2, permission="user_write" - ) - db_session.add(test_user_write_role_2) - - sa_update_domain = Domains(domain="sa.update.domain.ca", organization_id=1) - db_session.add(sa_update_domain) - sa_remove_domain = Domains(domain="sa.remove.domain.ca", organization_id=1) - db_session.add(sa_remove_domain) + sa_update_domain = Domains(domain="sa.update.domain.ca", organization_id=org1.id) + session.add(sa_update_domain) + sa_remove_domain = Domains(domain="sa.remove.domain.ca", organization_id=org1.id) + session.add(sa_remove_domain) org_admin_update_domain = Domains( - domain="admin.update.domain.ca", organization_id=1 + domain="admin.update.domain.ca", organization_id=org1.id ) - db_session.add(org_admin_update_domain) - org_admin_domain = Domains(domain="admin.remove.domain.ca", organization_id=1) - db_session.add(org_admin_domain) + session.add(org_admin_update_domain) + org_admin_domain = Domains(domain="admin.remove.domain.ca", organization_id=org1.id) + session.add(org_admin_domain) org_admin_update_domain2 = Domains( - domain="admin2.update.domain.ca", organization_id=1 + domain="admin2.update.domain.ca", organization_id=org1.id ) - db_session.add(org_admin_update_domain2) - org_admin_domain2 = Domains(domain="admin2.remove.domain.ca", organization_id=1) - db_session.add(org_admin_domain2) + session.add(org_admin_update_domain2) + org_admin_domain2 = Domains(domain="admin2.remove.domain.ca", organization_id=org1.id) + session.add(org_admin_domain2) user_write_update_domain = Domains( - domain="user.write.update.domain.ca", organization_id=1 + domain="user.write.update.domain.ca", organization_id=org1.id ) - db_session.add(user_write_update_domain) + session.add(user_write_update_domain) user_write_domain = Domains( - domain="user.write.remove.domain.ca", organization_id=1 + domain="user.write.remove.domain.ca", organization_id=org1.id ) - db_session.add(user_write_domain) + session.add(user_write_domain) user_write_update_domain_2 = Domains( - domain="user2.write.update.domain.ca", organization_id=1 + domain="user2.write.update.domain.ca", organization_id=org1.id ) - db_session.add(user_write_update_domain_2) + session.add(user_write_update_domain_2) user_write_domain_2 = Domains( - domain="user2.write.remove.domain.ca", organization_id=1 + domain="user2.write.remove.domain.ca", organization_id=org1.id ) - db_session.add(user_write_domain_2) + session.add(user_write_domain_2) user_read_update_domain = Domains( - domain="user.read.update.domain.ca", organization_id=1 + domain="user.read.update.domain.ca", organization_id=org1.id ) - db_session.add(user_read_update_domain) + session.add(user_read_update_domain) user_read_domain = Domains( - domain="user.read.remove.domain.ca", organization_id=1 + domain="user.read.remove.domain.ca", organization_id=org1.id ) - db_session.add(user_read_domain) - db_session.commit() + session.add(user_read_domain) + session.commit() # Super Admin Scans domain_id = ( - db_session.query(Domains) + session.query(Domains) .filter(Domains.domain == "sa.remove.domain.ca") .first() .id ) - sa_scan = Scans(id=1, domain_id=domain_id) - db_session.add(sa_scan) - db_session.commit() - sa_dkim = Dkim_scans(id=1) - db_session.add(sa_dkim) - sa_dmarc = Dmarc_scans(id=1) - db_session.add(sa_dmarc) - sa_https = Https_scans(id=1) - db_session.add(sa_https) - sa_ssl = Ssl_scans(id=1) - db_session.add(sa_ssl) - sa_spf = Spf_scans(id=1) - db_session.add(sa_spf) - db_session.commit() + sa_scan = Scans(domain_id=domain_id) + save(sa_scan) + sa_dkim = Dkim_scans(id=sa_scan.id) + session.add(sa_dkim) + sa_dmarc = Dmarc_scans(id=sa_scan.id) + session.add(sa_dmarc) + sa_https = Https_scans(id=sa_scan.id) + session.add(sa_https) + sa_ssl = Ssl_scans(id=sa_scan.id) + session.add(sa_ssl) + sa_spf = Spf_scans(id=sa_scan.id) + session.add(sa_spf) + session.commit() # Admin Scans domain_id = ( - db_session.query(Domains) + session.query(Domains) .filter(Domains.domain == "admin.remove.domain.ca") .first() .id ) - admin_scan = Scans(id=2, domain_id=domain_id) - db_session.add(admin_scan) - db_session.commit() - admin_dkim = Dkim_scans(id=2) - db_session.add(admin_dkim) - admin_dmarc = Dmarc_scans(id=2) - db_session.add(admin_dmarc) - admin_https = Https_scans(id=2) - db_session.add(admin_https) - admin_ssl = Ssl_scans(id=2) - db_session.add(admin_ssl) - admin_spf = Spf_scans(id=2) - db_session.add(admin_spf) - db_session.commit() + admin_scan = Scans(domain_id=domain_id) + session.add(admin_scan) + session.commit() + admin_dkim = Dkim_scans(id=admin_scan.id) + session.add(admin_dkim) + admin_dmarc = Dmarc_scans(id=admin_scan.id) + session.add(admin_dmarc) + admin_https = Https_scans(id=admin_scan.id) + session.add(admin_https) + admin_ssl = Ssl_scans(id=admin_scan.id) + session.add(admin_ssl) + admin_spf = Spf_scans(id=admin_scan.id) + session.add(admin_spf) + session.commit() # User Write Scans domain_id = ( - db_session.query(Domains) + session.query(Domains) .filter(Domains.domain == "user.write.remove.domain.ca") .first() .id ) - user_w_scan = Scans(id=3, domain_id=domain_id) - db_session.add(user_w_scan) - db_session.commit() - user_w_dkim = Dkim_scans(id=3) - db_session.add(user_w_dkim) - user_w_dmarc = Dmarc_scans(id=3) - db_session.add(user_w_dmarc) - user_w_https = Https_scans(id=3) - db_session.add(user_w_https) - user_w_ssl = Ssl_scans(id=3) - db_session.add(user_w_ssl) - user_w_spf = Spf_scans(id=3) - db_session.add(user_w_spf) - db_session.commit() - - yield + user_w_scan = Scans(domain_id=domain_id) + session.add(user_w_scan) + session.commit() + user_w_dkim = Dkim_scans(id=user_w_scan.id) + session.add(user_w_dkim) + user_w_dmarc = Dmarc_scans(id=user_w_scan.id) + session.add(user_w_dmarc) + user_w_https = Https_scans(id=user_w_scan.id) + session.add(user_w_https) + user_w_ssl = Ssl_scans(id=user_w_scan.id) + session.add(user_w_ssl) + user_w_spf = Spf_scans(id=user_w_scan.id) + session.add(user_w_spf) + session.commit() + + yield + cleanup() - with app.app_context(): - Ssl_scans.query.delete() - Spf_scans.query.delete() - Mx_scans.query.delete() - Https_scans.query.delete() - Dmarc_scans.query.delete() - Dkim_scans.query.delete() - Scans.query.delete() - Domains.query.delete() - User_affiliations.query.delete() - Organizations.query.delete() - Users.query.delete() - db_session.commit() @pytest.mark.usefixtures("domain_test_db_init") @@ -378,17 +366,17 @@ def test_domain_removal_super_admin(self): assert executed["data"]["removeDomain"]["status"] assert ( - not db_session.query(Domains) + not session.query(Domains) .filter(Domains.domain == "sa.remove.domain.ca") .all() ) - assert not db_session.query(Scans).filter(Scans.id == 1).all() - assert not db_session.query(Dkim_scans).filter(Dkim_scans.id == 1).all() - assert not db_session.query(Dmarc_scans).filter(Dmarc_scans.id == 1).all() - assert not db_session.query(Https_scans).filter(Https_scans.id == 1).all() - assert not db_session.query(Mx_scans).filter(Mx_scans.id == 1).all() - assert not db_session.query(Ssl_scans).filter(Ssl_scans.id == 1).all() - assert not db_session.query(Spf_scans).filter(Spf_scans.id == 1).all() + assert not session.query(Scans).filter(Scans.id == 1).all() + assert not session.query(Dkim_scans).filter(Dkim_scans.id == 1).all() + assert not session.query(Dmarc_scans).filter(Dmarc_scans.id == 1).all() + assert not session.query(Https_scans).filter(Https_scans.id == 1).all() + assert not session.query(Mx_scans).filter(Mx_scans.id == 1).all() + assert not session.query(Ssl_scans).filter(Ssl_scans.id == 1).all() + assert not session.query(Spf_scans).filter(Spf_scans.id == 1).all() def test_domain_creation_super_admin_acronym_sa(self): """ @@ -588,17 +576,17 @@ def test_domain_removal_org_admin(self): assert executed["data"]["removeDomain"]["status"] assert ( - not db_session.query(Domains) + not session.query(Domains) .filter(Domains.domain == "admin.remove.domain.ca") .all() ) - assert not db_session.query(Scans).filter(Scans.id == 2).all() - assert not db_session.query(Dkim_scans).filter(Dkim_scans.id == 2).all() - assert not db_session.query(Dmarc_scans).filter(Dmarc_scans.id == 2).all() - assert not db_session.query(Https_scans).filter(Https_scans.id == 2).all() - assert not db_session.query(Mx_scans).filter(Mx_scans.id == 2).all() - assert not db_session.query(Ssl_scans).filter(Ssl_scans.id == 2).all() - assert not db_session.query(Spf_scans).filter(Spf_scans.id == 2).all() + assert not session.query(Scans).filter(Scans.id == 2).all() + assert not session.query(Dkim_scans).filter(Dkim_scans.id == 2).all() + assert not session.query(Dmarc_scans).filter(Dmarc_scans.id == 2).all() + assert not session.query(Https_scans).filter(Https_scans.id == 2).all() + assert not session.query(Mx_scans).filter(Mx_scans.id == 2).all() + assert not session.query(Ssl_scans).filter(Ssl_scans.id == 2).all() + assert not session.query(Spf_scans).filter(Spf_scans.id == 2).all() # Different Org Admin def test_domain_creation_diff_org_admin(self): @@ -786,7 +774,7 @@ def test_domain_creation_user_write(self): backend=backend, ) result_refr = {"data": {"domain": [{"url": "user.write.create.domain.ca"}]}} - self.assertDictEqual(result_refr, executed) + assert result_refr == executed def test_domain_modification_user_write(self): """ @@ -904,17 +892,17 @@ def test_domain_removal_user_write(self): assert executed["errors"][0]["message"] == "Error, domain does not exist" assert ( - not db_session.query(Domains) + not session.query(Domains) .filter(Domains.domain == "user.write.remove.domain.ca") .all() ) - assert not db_session.query(Scans).filter(Scans.id == 3).all() - assert not db_session.query(Dkim_scans).filter(Dkim_scans.id == 3).all() - assert not db_session.query(Dmarc_scans).filter(Dmarc_scans.id == 3).all() - assert not db_session.query(Https_scans).filter(Https_scans.id == 3).all() - assert not db_session.query(Mx_scans).filter(Mx_scans.id == 3).all() - assert not db_session.query(Ssl_scans).filter(Ssl_scans.id == 3).all() - assert not db_session.query(Spf_scans).filter(Spf_scans.id == 3).all() + assert not session.query(Scans).filter(Scans.id == 3).all() + assert not session.query(Dkim_scans).filter(Dkim_scans.id == 3).all() + assert not session.query(Dmarc_scans).filter(Dmarc_scans.id == 3).all() + assert not session.query(Https_scans).filter(Https_scans.id == 3).all() + assert not session.query(Mx_scans).filter(Mx_scans.id == 3).all() + assert not session.query(Ssl_scans).filter(Ssl_scans.id == 3).all() + assert not session.query(Spf_scans).filter(Spf_scans.id == 3).all() # Different Org User Write def test_domain_creation_diff_org_user_write(self): diff --git a/api/tests/test_domains_resolver_access_control.py b/api/tests/test_domains_resolver_access_control.py index 4ffd527dca..b35ea0e9ea 100644 --- a/api/tests/test_domains_resolver_access_control.py +++ b/api/tests/test_domains_resolver_access_control.py @@ -4,11 +4,12 @@ from unittest import TestCase from werkzeug.test import create_environ from app import app -from db import db_session +from db import DB from models import Organizations, Domains, Users, User_affiliations from queries import schema from backend.security_check import SecurityAnalysisBackend +_, cleanup, db_session = DB() @pytest.fixture(scope="class") def domain_test_db_init(): @@ -58,14 +59,9 @@ def domain_test_db_init(): db_session.add(domain) db_session.commit() - yield + yield - with app.app_context(): - Domains.query.delete() - User_affiliations.query.delete() - Organizations.query.delete() - Users.query.delete() - db_session.commit() + cleanup() @pytest.mark.usefixtures("domain_test_db_init") diff --git a/api/tests/test_domains_resolver_values.py b/api/tests/test_domains_resolver_values.py index e1bcfa9067..9bc348351c 100644 --- a/api/tests/test_domains_resolver_values.py +++ b/api/tests/test_domains_resolver_values.py @@ -1,12 +1,11 @@ import pytest +from pytest import fail +from json_web_token import tokenize, auth_header from flask import Request from graphene.test import Client -from unittest import TestCase -from werkzeug.test import create_environ from app import app -from db import db_session +from db import DB from queries import schema -from backend.security_check import SecurityAnalysisBackend from models import ( Organizations, Domains, @@ -21,863 +20,418 @@ Spf_scans, Ssl_scans, ) +from tests.testdata import accurateplastics_report +s, cleanup, session = DB() -@pytest.fixture(scope="class") -def domain_test_db_init(): +@pytest.fixture +def save(): with app.app_context(): - test_user = Users( - id=1, - display_name="testuserread", - user_name="testuserread@testemail.ca", - password="testpassword123", - ) - db_session.add(test_user) + yield s + session.rollback() + cleanup() - org = Organizations( - id=1, acronym="ORG1", org_tags={"description": "Organization 1"} - ) - db_session.add(org) - test_user_read_role = User_affiliations( - user_id=1, organization_id=1, permission="user_read" - ) - db_session.add(test_user_read_role) - - domain = Domains(id=1, domain="accurateplastics.com", organization_id=1) - db_session.add(domain) - domain = Domains(id=2, domain="addisonfoods.com", organization_id=1) - db_session.add(domain) - - test_dmarc_report = Dmarc_Reports( - id=1, - domain_id=1, - start_date="2018-10-01 13:07:12", - end_date="2018-10-01 13:07:12", - report={ - "xml_schema": "draft", - "report_metadata": { - "org_name": "accurateplastics.com", - "org_email": "administrator@accurateplastics.com", - "org_extra_contact_info": "null", - "report_id": "example.com:1538463741", - "begin_date": "2018-10-01 13:07:12", - "end_date": "2018-10-01 13:07:12", - "errors": [ - "Invalid XML: not well-formed (invalid token): line 5, column 17" - ], - }, - "policy_published": { - "domain": "example.com", - "adkim": "r", - "aspf": "r", - "p": "none", - "sp": "reject", - "pct": "100", - "fo": "0", - }, - "records": [ - { - "source": { - "ip_address": "12.20.127.122", - "country": "US", - "reverse_dns": "null", - "base_domain": "null", - }, - "count": 1, - "alignment": {"spf": False, "dkim": False, "dmarc": False}, - "policy_evaluated": { - "disposition": "none", - "dkim": "fail", - "spf": "fail", - "policy_override_reasons": ["TESTING TEXT"], - }, - "identifiers": { - "header_from": "example.com", - "envelope_from": "null", - "envelope_to": "null", - }, - "auth_results": { - "dkim": [ - { - "domain": "toptierhighticket.club", - "selector": "default", - "result": "pass", - } - ], - "spf": [ - {"domain": "null", "scope": "mfrom", "result": "none"} +def test_get_domain_resolver_dmarc_report(save): + """ + Test to see if all values appear + """ + user = Users( + display_name="testuserread", + user_name="testuserread@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations( + permission="user_read", + user_organization=Organizations( + acronym="ORG1", + domains=[ + Domains( + domain="accurateplastics.com", + dmarc_reports=[ + Dmarc_Reports( + start_date="2018-10-01 13:07:12", + end_date="2018-10-01 13:07:12", + report=accurateplastics_report, + ) ], - }, - } - ], - }, - ) - db_session.add(test_dmarc_report) - db_session.commit() + ), + Domains(domain="addisonfoods.com"), + ], + ), + ) + ], + ) + save(user) - yield + token = tokenize(user_id=user.id, roles=user.roles) - with app.app_context(): - Dkim_scans.query.delete() - Dmarc_scans.query.delete() - Https_scans.query.delete() - Mx_scans.query.delete() - Spf_scans.query.delete() - Ssl_scans.query.delete() - Scans.query.delete() - Dmarc_Reports.query.delete() - Domains.query.delete() - User_affiliations.query.delete() - Organizations.query.delete() - Users.query.delete() - db_session.commit() - - -@pytest.mark.usefixtures("domain_test_db_init") -class TestDomainsResolver(TestCase): - def test_get_domain_resolver_dmarc_report(self): - """ - Test to see if all values appear + result = Client(schema).execute( """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken + { + domain(url: "accurateplastics.com") { + url + dmarcReport { + edges { + node { + reportId + orgName + orgEmail + startDate + endDate + } } } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None + } + } + """, + context_value=auth_header(token), + ) - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) + if "errors" in result: + fail("Expected dmarcReport query to succeed. Instead: {}".format(result)) - executed = client.execute( - """ + expected = { + "data": { + "domain": [ { - domain(url: "accurateplastics.com") { - url - dmarcReport { - edges { - node { - reportId - orgName - orgEmail - startDate - endDate - errors - policyPublished { - domain - adkim - p - sp - pct - fo - } - records { - count - source { - ipAddress - country - reverseDns - baseDomain - } - alignment { - spf - dkim - dmarc - } - policyEvaluated { - disposition - dkim - spf - policyOverrideReasons - } - identifiers { - headerFrom - envelopeFrom - envelopeTo - } - authResults { - dkim { - domain - selector - result - } - spf { - domain - scope - result - } - } - } + "url": "accurateplastics.com", + "dmarcReport": { + "edges": [ + { + "node": { + "reportId": "example.com:1538463741", + "orgName": "accurateplastics.com", + "orgEmail": "administrator@accurateplastics.com", + "startDate": "2018-10-01T13:07:12", + "endDate": "2018-10-01T13:07:12", } } - } - } + ] + }, } - """, - context_value=request_headers, - backend=backend, + ] + } + } + assert expected == result + + +def test_get_domain_resolver_dmarc_report_in_date_range(save): + """ + Test to see if all values appear + """ + user = Users( + display_name="testuserread", + user_name="testuserread@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations( + permission="user_read", + user_organization=Organizations( + acronym="ORG1", + domains=[ + Domains( + domain="accurateplastics.com", + dmarc_reports=[ + Dmarc_Reports( + start_date="2018-10-01 13:07:12", + end_date="2018-10-01 13:07:12", + report=accurateplastics_report, + ) + ], + ), + ], + ), ) - result_refr = { - "data": { - "domain": [ - { - "url": "accurateplastics.com", - "dmarcReport": { - "edges": [ - { - "node": { - "reportId": "example.com:1538463741", - "orgName": "accurateplastics.com", - "orgEmail": "administrator@accurateplastics.com", - "startDate": "2018-10-01T13:07:12", - "endDate": "2018-10-01T13:07:12", - "errors": [ - "Invalid XML: not well-formed (invalid token): line 5, column 17" - ], - "policyPublished": { - "domain": "example.com", - "adkim": "r", - "p": "none", - "sp": "reject", - "pct": 100, - "fo": 0, - }, - "records": [ - { - "count": 1, - "source": { - "ipAddress": "12.20.127.122", - "country": "US", - "reverseDns": "null", - "baseDomain": "null", - }, - "alignment": { - "spf": False, - "dkim": False, - "dmarc": False, - }, - "policyEvaluated": { - "disposition": "none", - "dkim": "fail", - "spf": "fail", - "policyOverrideReasons": [ - "TESTING TEXT" - ], - }, - "identifiers": { - "headerFrom": "example.com", - "envelopeFrom": "null", - "envelopeTo": "null", - }, - "authResults": { - "dkim": [ - { - "domain": "toptierhighticket.club", - "selector": "default", - "result": "pass", - } - ], - "spf": [ - { - "domain": "null", - "scope": "mfrom", - "result": "none", - } - ], - }, - } - ], - } - } - ] - }, - } - ] - } - } - self.assertDictEqual(result_refr, executed) + ], + ) + save(user) - def test_get_domain_resolver_dmarc_report_in_date_range(self): - """ - Test to see if all values appear within the date range + token = tokenize(user_id=user.id, roles=user.roles) + + result = Client(schema).execute( """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None + { + domain(url: "accurateplastics.com") { + url + dmarcReport(startDate: "2018-01-01" endDate: "2018-12-31") { + edges { + node { + reportId + orgName + orgEmail + startDate + endDate + } + } + } + } + } + """, + context_value=auth_header(token), + ) - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) + if "errors" in result: + fail("Expected dmarcReport query to succeed. Instead: {}".format(result)) - executed = client.execute( - """ + expected = { + "data": { + "domain": [ { - domain(url: "accurateplastics.com") { - url - dmarcReport(startDate: "2018-01-01" endDate: "2018-12-31") { - edges { - node { - reportId - orgName - orgEmail - startDate - endDate - errors - policyPublished { - domain - adkim - p - sp - pct - fo - } - records { - count - source { - ipAddress - country - reverseDns - baseDomain - } - alignment { - spf - dkim - dmarc - } - policyEvaluated { - disposition - dkim - spf - policyOverrideReasons - } - identifiers { - headerFrom - envelopeFrom - envelopeTo - } - authResults { - dkim { - domain - selector - result - } - spf { - domain - scope - result - } - } - } + "url": "accurateplastics.com", + "dmarcReport": { + "edges": [ + { + "node": { + "reportId": "example.com:1538463741", + "orgName": "accurateplastics.com", + "orgEmail": "administrator@accurateplastics.com", + "startDate": "2018-10-01T13:07:12", + "endDate": "2018-10-01T13:07:12", } } - } - } + ] + }, } - """, - context_value=request_headers, - backend=backend, + ] + } + } + assert expected == result + + +def test_get_domain_resolver_dmarc_report_for_date_range_with_no_reports(save): + user = Users( + display_name="testuserread", + user_name="testuserread@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations( + permission="user_read", + user_organization=Organizations( + acronym="ORG1", + domains=[ + Domains( + domain="accurateplastics.com", + dmarc_reports=[ + Dmarc_Reports( + start_date="2018-10-01 13:07:12", + end_date="2018-10-01 13:07:12", + report=accurateplastics_report, + ) + ], + ), + ], + ), ) - result_refr = { - "data": { - "domain": [ - { - "url": "accurateplastics.com", - "dmarcReport": { - "edges": [ - { - "node": { - "reportId": "example.com:1538463741", - "orgName": "accurateplastics.com", - "orgEmail": "administrator@accurateplastics.com", - "startDate": "2018-10-01T13:07:12", - "endDate": "2018-10-01T13:07:12", - "errors": [ - "Invalid XML: not well-formed (invalid token): line 5, column 17" - ], - "policyPublished": { - "domain": "example.com", - "adkim": "r", - "p": "none", - "sp": "reject", - "pct": 100, - "fo": 0, - }, - "records": [ - { - "count": 1, - "source": { - "ipAddress": "12.20.127.122", - "country": "US", - "reverseDns": "null", - "baseDomain": "null", - }, - "alignment": { - "spf": False, - "dkim": False, - "dmarc": False, - }, - "policyEvaluated": { - "disposition": "none", - "dkim": "fail", - "spf": "fail", - "policyOverrideReasons": [ - "TESTING TEXT" - ], - }, - "identifiers": { - "headerFrom": "example.com", - "envelopeFrom": "null", - "envelopeTo": "null", - }, - "authResults": { - "dkim": [ - { - "domain": "toptierhighticket.club", - "selector": "default", - "result": "pass", - } - ], - "spf": [ - { - "domain": "null", - "scope": "mfrom", - "result": "none", - } - ], - }, - } - ], - } - } - ] - }, - } - ] - } - } - self.assertDictEqual(result_refr, executed) + ], + ) + save(user) - def test_get_domain_resolver_dmarc_report_out_of_date_range(self): - """ - Test to see if no values appear out of date range + token = tokenize(user_id=user.id, roles=user.roles) + + result = Client(schema).execute( """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, + { + domain(url: "accurateplastics.com") { + url + dmarcReport(startDate: "2019-01-01" endDate: "2019-12-31") { + edges { + node { + reportId + orgName + orgEmail + startDate + endDate + } + } + } + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "Expected dmarcReport query for report dates we don't have to fail. Instead: {}".format( + result ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None + ) - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - executed = client.execute( - """ - { - domain(url: "accurateplastics.com") { - url - dmarcReport(startDate: "2019-01-01" endDate: "2019-12-31") { - edges { - node { - reportId - orgName - orgEmail - startDate - endDate - errors - policyPublished { - domain - adkim - p - sp - pct - fo - } - records { - count - source { - ipAddress - country - reverseDns - baseDomain - } - alignment { - spf - dkim - dmarc - } - policyEvaluated { - disposition - dkim - spf - policyOverrideReasons - } - identifiers { - headerFrom - envelopeFrom - envelopeTo - } - authResults { - dkim { - domain - selector - result - } - spf { - domain - scope - result - } - } - } - } - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, no reports for that domain in that date range." +def test_domain_resolver_dmarc_report_for_domain_with_no_reports_will_fail(save): + user = Users( + display_name="testuserread", + user_name="testuserread@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations( + permission="user_read", + user_organization=Organizations( + acronym="ORG1", domains=[Domains(domain="addisonfoods.com"),], + ), ) + ], + ) + save(user) - def test_get_domain_resolver_dmarc_report_domain_missing_report(self): - """ - Test to see if no values appear out when domain has no report + token = tokenize(user_id=user.id, roles=user.roles) + + result = Client(schema).execute( """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, + { + domain(url: "addisonfoods.com") { + url + dmarcReport { + edges { + node { + reportId + orgName + orgEmail + startDate + endDate + } + } + } + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "Expected dmarcReport query for report dates we don't have to fail. Instead: {}".format( + result ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None + ) - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) + [error] = result["errors"] + assert error["message"] == "Error, no reports for that domain." - executed = client.execute( - """ - { - domain(url: "addisonfoods.com") { - url - dmarcReport { - edges { - node { - reportId - orgName - orgEmail - startDate - endDate - errors - policyPublished { - domain - adkim - p - sp - pct - fo - } - records { - count - source { - ipAddress - country - reverseDns - baseDomain - } - alignment { - spf - dkim - dmarc - } - policyEvaluated { - disposition - dkim - spf - policyOverrideReasons - } - identifiers { - headerFrom - envelopeFrom - envelopeTo - } - authResults { - dkim { - domain - selector - result - } - spf { - domain - scope - result - } - } - } - } - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] == "Error, no reports for that domain." + +def test_get_domain_resolver_dmarc_report_returns_error_if_start_date_is_missing(save): + user = Users( + display_name="testuserread", + user_name="testuserread@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations( + permission="user_read", + user_organization=Organizations( + acronym="ORG1", + domains=[ + Domains( + domain="accurateplastics.com", + dmarc_reports=[ + Dmarc_Reports( + start_date="2018-10-01 13:07:12", + end_date="2018-10-01 13:07:12", + report=accurateplastics_report, + ) + ], + ), + ], + ), ) + ], + ) + save(user) - def test_get_domain_resolver_dmarc_report_no_start_date(self): - """ - Test to see if no values appear when start date missing + token = tokenize(user_id=user.id, roles=user.roles) + + result = Client(schema).execute( """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, + { + domain(url: "accurateplastics.com") { + url + dmarcReport(endDate: "2019-12-31") { + edges { + node { + reportId + orgName + orgEmail + startDate + endDate + } + } + } + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "Expected dmarcReport query with no start date to fail. Instead: {}".format( + result ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None + ) - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) + [error] = result["errors"] + assert error["message"] == "Error, both start and end dates are required." - executed = client.execute( - """ - { - domain(url: "addisonfoods.com") { - url - dmarcReport(endDate: "2018-01-01") { - edges { - node { - reportId - orgName - orgEmail - startDate - endDate - errors - policyPublished { - domain - adkim - p - sp - pct - fo - } - records { - count - source { - ipAddress - country - reverseDns - baseDomain - } - alignment { - spf - dkim - dmarc - } - policyEvaluated { - disposition - dkim - spf - policyOverrideReasons - } - identifiers { - headerFrom - envelopeFrom - envelopeTo - } - authResults { - dkim { - domain - selector - result - } - spf { - domain - scope - result - } - } - } - } - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, both start and end dates are required." - ) - def test_get_domain_resolver_dmarc_report_no_end_date(self): - """ - Test to see if no values appear when end date missing - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, +def test_get_domain_resolver_dmarc_report_returns_error_if_end_date_is_missing(save): + user = Users( + display_name="testuserread", + user_name="testuserread@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations( + permission="user_read", + user_organization=Organizations( + acronym="ORG1", + domains=[ + Domains( + domain="accurateplastics.com", + dmarc_reports=[ + Dmarc_Reports( + start_date="2018-10-01 13:07:12", + end_date="2018-10-01 13:07:12", + report=accurateplastics_report, + ) + ], + ), + ], + ), ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None + ], + ) + save(user) - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) + token = tokenize(user_id=user.id, roles=user.roles) - executed = client.execute( - """ - { - domain(url: "addisonfoods.com") { - url - dmarcReport(startDate: "2018-01-01") { - edges { - node { - reportId - orgName - orgEmail - startDate - endDate - errors - policyPublished { - domain - adkim - p - sp - pct - fo - } - records { - count - source { - ipAddress - country - reverseDns - baseDomain - } - alignment { - spf - dkim - dmarc - } - policyEvaluated { - disposition - dkim - spf - policyOverrideReasons - } - identifiers { - headerFrom - envelopeFrom - envelopeTo - } - authResults { - dkim { - domain - selector - result - } - spf { - domain - scope - result - } - } - } - } - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, both start and end dates are required." + result = Client(schema).execute( + """ + { + domain(url: "accurateplastics.com") { + url + dmarcReport(startDate: "2019-12-31") { + edges { + node { + reportId + orgName + orgEmail + startDate + endDate + } + } + } + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "Expected dmarcReport query with no start date to fail. Instead: {}".format( + result ) + ) + + [error] = result["errors"] + assert error["message"] == "Error, both start and end dates are required." diff --git a/api/tests/test_organization_mutations.py b/api/tests/test_organization_mutations.py deleted file mode 100644 index 52596f772f..0000000000 --- a/api/tests/test_organization_mutations.py +++ /dev/null @@ -1,1102 +0,0 @@ -import pytest -from flask import Request -from graphene.test import Client -from unittest import TestCase -from werkzeug.test import create_environ -from app import app -from db import db_session -from queries import schema -from backend.security_check import SecurityAnalysisBackend -from models import ( - Organizations, - Domains, - Users, - User_affiliations, - Scans, - Dkim_scans, - Dmarc_scans, - Https_scans, - Mx_scans, - Spf_scans, - Ssl_scans, -) - - -@pytest.fixture(scope="class") -def organization_mutation_db_init(): - with app.app_context(): - test_super_admin = Users( - id=1, - display_name="testsuperadmin", - user_name="testsuperadmin@testemail.ca", - password="testpassword123", - ) - db_session.add(test_super_admin) - test_admin = Users( - id=2, - display_name="testadmin", - user_name="testadmin@testemail.ca", - password="testpassword123", - ) - db_session.add(test_admin) - test_write = Users( - id=3, - display_name="testuserwrite", - user_name="testuserwrite@testemail.ca", - password="testpassword123", - ) - db_session.add(test_write) - test_read = Users( - id=4, - display_name="testuserread", - user_name="testuserread@testemail.ca", - password="testpassword123", - ) - db_session.add(test_read) - - # Super Admin Test Values - sa_org = Organizations(acronym="SA") - db_session.add(sa_org) - db_session.commit() - org_orm = ( - db_session.query(Organizations) - .filter(Organizations.acronym == "SA") - .first() - ) - sa_user_aff = User_affiliations( - organization_id=org_orm.id, user_id=1, permission="super_admin" - ) - db_session.add(sa_user_aff) - db_session.commit() - sa_check_org = Organizations(acronym="SA_CHECK") - db_session.add(sa_check_org) - sa_update_org = Organizations(acronym="SA_UPDATE") - db_session.add(sa_update_org) - sa_delete_org = Organizations(acronym="SA_DELETE") - db_session.add(sa_delete_org) - db_session.commit() - org_orm = ( - db_session.query(Organizations) - .filter(Organizations.acronym == "SA_DELETE") - .first() - ) - sa_domain = Domains(id=1, organization_id=org_orm.id) - db_session.add(sa_domain) - sa_scan = Scans(id=1, domain_id=1) - db_session.add(sa_scan) - db_session.commit() - sa_dkim = Dkim_scans(id=1) - db_session.add(sa_dkim) - sa_dmarc = Dmarc_scans(id=1) - db_session.add(sa_dmarc) - sa_https = Https_scans(id=1) - db_session.add(sa_https) - sa_mx = Mx_scans(id=1) - db_session.add(sa_mx) - sa_spf = Spf_scans(id=1) - db_session.add(sa_spf) - sa_ssl = Ssl_scans(id=1) - db_session.add(sa_ssl) - db_session.commit() - - # Admin Db Inserts - admin_org = Organizations(acronym="ADMIN_ORG") - db_session.add(admin_org) - db_session.commit() - org_orm = ( - db_session.query(Organizations) - .filter(Organizations.acronym == "ADMIN_ORG") - .first() - ) - admin_aff = User_affiliations( - organization_id=org_orm.id, user_id=2, permission="admin" - ) - db_session.add(admin_aff) - - # User Write Db Inserts - user_write_org = Organizations(acronym="USER_W_ORG") - db_session.add(user_write_org) - db_session.commit() - org_orm = ( - db_session.query(Organizations) - .filter(Organizations.acronym == "USER_W_ORG") - .first() - ) - user_write_aff = User_affiliations( - organization_id=org_orm.id, user_id=3, permission="user_write" - ) - db_session.add(user_write_aff) - - # User Read Db Inserts - user_read_org = Organizations(acronym="USER_R_ORG") - db_session.add(user_read_org) - db_session.commit() - org_orm = ( - db_session.query(Organizations) - .filter(Organizations.acronym == "USER_R_ORG") - .first() - ) - user_read_aff = User_affiliations( - organization_id=org_orm.id, user_id=4, permission="user_read" - ) - db_session.add(user_read_aff) - db_session.commit() - - yield - - with app.app_context(): - Dkim_scans.query.delete() - Dmarc_scans.query.delete() - Https_scans.query.delete() - Mx_scans.query.delete() - Spf_scans.query.delete() - Ssl_scans.query.delete() - Scans.query.delete() - Domains.query.delete() - User_affiliations.query.delete() - Organizations.query.delete() - Users.query.delete() - db_session.commit() - - -@pytest.mark.usefixtures("organization_mutation_db_init") -class TestOrganizationMutations(TestCase): - # Super Admin Tests - def test_sa_org_mutation_create_org(self): - """ - Test To See If SA Can Create Organization - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - createOrganization( - acronym: "TEST_ORG" - description: "Test Organization" - zone: "Test Zone" - sector: "Test Sector" - province: "Nova Scotia" - city: "Halifax" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["data"] - assert executed["data"]["createOrganization"] - assert executed["data"]["createOrganization"]["status"] - - executed = client.execute( - """ - { - organization(org: "TEST_ORG") { - edges { - node { - acronym - description - zone - sector - province - city - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - result_refr = { - "data": { - "organization": { - "edges": [ - { - "node": { - "acronym": "TEST_ORG", - "description": "Test Organization", - "zone": "Test Zone", - "sector": "Test Sector", - "province": "Nova Scotia", - "city": "Halifax", - } - } - ] - } - } - } - self.assertDictEqual(result_refr, executed) - - def test_sa_org_mutation_create_org_already_exists(self): - """ - Test Error Check For Existing Org - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - createOrganization( - acronym: "SA_CHECK" - description: "Test Organization" - zone: "Test Zone" - sector: "Test Sector" - province: "Nova Scotia" - city: "Halifax" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] == "Error, Organization alredy exists" - ) - - def test_sa_org_mutation_update_org(self): - """ - Test To See If SA Can Update an Organization - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - updateOrganization( - acronym: "TEST_ORG" - updatedAcronym: "NEW_ORG" - description: "Test Organization" - zone: "Test Zone" - sector: "Test Sector" - province: "Nova Scotia" - city: "Halifax" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["data"] - assert executed["data"]["updateOrganization"] - assert executed["data"]["updateOrganization"]["status"] - - executed = client.execute( - """ - { - organization(org: "NEW_ORG") { - edges { - node { - acronym - description - zone - sector - province - city - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - result_refr = { - "data": { - "organization": { - "edges": [ - { - "node": { - "acronym": "NEW_ORG", - "description": "Test Organization", - "zone": "Test Zone", - "sector": "Test Sector", - "province": "Nova Scotia", - "city": "Halifax", - } - } - ] - } - } - } - self.assertDictEqual(result_refr, executed) - - def test_sa_org_mutation_update_org_acronym_already_in_use(self): - """ - Test To See Error appears when acronym already taken - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - updateOrganization( - acronym: "SA_UPDATE" - updatedAcronym: "SA" - description: "Test Organization" - zone: "Test Zone" - sector: "Test Sector" - province: "Nova Scotia" - city: "Halifax" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert executed["errors"][0]["message"] == "Error, acronym already in use." - - def test_sa_org_mutation_update_org_does_not_exist(self): - """ - Test To See Error appears when org does not exist - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - updateOrganization( - acronym: "TEST_ORG" - updatedAcronym: "SA" - description: "Test Organization" - zone: "Test Zone" - sector: "Test Sector" - province: "Nova Scotia" - city: "Halifax" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, organization does not exist." - ) - - def test_sa_org_mutation_remove_org(self): - """ - Test To See If SA Can Remove an Organization - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - org_id = ( - db_session.query(Organizations) - .filter(Organizations.acronym == "SA_DELETE") - .first() - .id - ) - - executed = client.execute( - """ - mutation { - removeOrganization( - acronym: "SA_DELETE" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["data"] - assert executed["data"]["removeOrganization"] - assert executed["data"]["removeOrganization"]["status"] - - check_org_orm = ( - db_session.query(Organizations) - .filter(Organizations.acronym == "SA_DELETE") - .first() - ) - assert check_org_orm is None - - check_user_aff = ( - db_session.query(User_affiliations) - .filter(User_affiliations.organization_id == org_id) - .all() - ) - assert not check_user_aff - - assert not db_session.query(Domains).all() - assert not db_session.query(Scans).all() - assert not db_session.query(Ssl_scans).all() - assert not db_session.query(Spf_scans).all() - assert not db_session.query(Mx_scans).all() - assert not db_session.query(Https_scans).all() - assert not db_session.query(Dmarc_scans).all() - assert not db_session.query(Dkim_scans).all() - - def test_sa_org_mutation_remove_org_sa_org(self): - """ - Test To See If SA Can't Remove SA Organization - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - removeOrganization( - acronym: "SA" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you cannot remove this organization" - ) - - def test_sa_org_mutation_remove_org_does_not_exist(self): - """ - Test Error appears when trying to remove an org that doesn't exist - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - removeOrganization( - acronym: "RANDOM" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] == "Error, organization does not exist" - ) - - # Admin Test - def test_admin_org_mutation_create_org_fail(self): - """ - Test That Admin can't create orgs - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - createOrganization( - acronym: "ADMIN_NEW" - description: "Test Organization" - zone: "Test Zone" - sector: "Test Sector" - province: "Nova Scotia" - city: "Halifax" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you do not have permission to create organizations" - ) - - def test_admin_org_mutation_update_org_fail(self): - """ - Test That Admin can't update orgs - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - updateOrganization( - acronym: "ADMIN_ORG" - description: "Test Organization" - zone: "Test Zone" - sector: "Test Sector" - province: "Nova Scotia" - city: "Halifax" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you do not have permission to update organizations" - ) - - def test_admin_org_mutation_remove_org_fail(self): - """ - Test That Admin can't remove orgs - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - removeOrganization( - acronym: "ADMIN_ORG" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you do not have permission to remove organizations." - ) - - # User Write Test - def test_user_write_org_mutation_create_org_fail(self): - """ - Test That User Write can't create orgs - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserwrite@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - createOrganization( - acronym: "USER_W_NEW" - description: "Test Organization" - zone: "Test Zone" - sector: "Test Sector" - province: "Nova Scotia" - city: "Halifax" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you do not have permission to create organizations" - ) - - def test_user_write_org_mutation_update_org_fail(self): - """ - Test That User Write can't update orgs - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserwrite@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - updateOrganization( - acronym: "USER_W_ORG" - description: "Test Organization" - zone: "Test Zone" - sector: "Test Sector" - province: "Nova Scotia" - city: "Halifax" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you do not have permission to update organizations" - ) - - def test_user_write_org_mutation_remove_org_fail(self): - """ - Test That User Write can't remove orgs - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserwrite@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - removeOrganization( - acronym: "USER_W_ORG" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you do not have permission to remove organizations." - ) - - # User Read Test - def test_user_read_org_mutation_create_org_fail(self): - """ - Test That user read can't create orgs - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - createOrganization( - acronym: "USER_R_NEW" - description: "Test Organization" - zone: "Test Zone" - sector: "Test Sector" - province: "Nova Scotia" - city: "Halifax" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you do not have permission to create organizations" - ) - - def test_user_read_org_mutation_update_org_fail(self): - """ - Test That user read can't update orgs - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - updateOrganization( - acronym: "USER_R_ORG" - description: "Test Organization" - zone: "Test Zone" - sector: "Test Sector" - province: "Nova Scotia" - city: "Halifax" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you do not have permission to update organizations" - ) - - def test_user_read_org_mutation_remove_org_fail(self): - """ - Test That user read can't remove orgs - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - mutation { - removeOrganization( - acronym: "USER_R_ORG" - ) { - status - } - } - """, - context_value=request_headers, - backend=backend, - ) - - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you do not have permission to remove organizations." - ) diff --git a/api/tests/test_organization_resolver_access_control.py b/api/tests/test_organization_resolver_access_control.py index 9b539ceae6..d3345174c4 100644 --- a/api/tests/test_organization_resolver_access_control.py +++ b/api/tests/test_organization_resolver_access_control.py @@ -1,419 +1,228 @@ import pytest +import json from flask import Request +from json_web_token import tokenize, auth_header from graphene.test import Client from unittest import TestCase from werkzeug.test import create_environ from app import app -from db import db_session +from db import DB from models import Organizations, Users, User_affiliations from queries import schema from backend.security_check import SecurityAnalysisBackend -@pytest.fixture(scope="class") -def org_perm_test_db_init(): - with app.app_context(): - test_user = Users( - id=1, - display_name="testuserread", - user_name="testuserread@testemail.ca", - password="testpassword123", - ) - db_session.add(test_user) - test_super_admin = Users( - id=2, - display_name="testsuperadmin", - user_name="testsuperadmin@testemail.ca", - password="testpassword123", - ) - db_session.add(test_super_admin) +s, cleanup, session = DB() - org = Organizations( - id=1, acronym="ORG1", org_tags={"description": "Organization 1"} - ) - db_session.add(org) - org = Organizations( - id=2, acronym="ORG2", org_tags={"description": "Organization 2"} - ) - db_session.add(org) - org = Organizations( - id=3, acronym="ORG3", org_tags={"description": "Organization 3"} - ) - db_session.add(org) - - test_admin_role = User_affiliations( - user_id=1, organization_id=1, permission="user_read" - ) - db_session.add(test_admin_role) - test_admin_role = User_affiliations( - user_id=2, organization_id=1, permission="super_admin" - ) - db_session.add(test_admin_role) - db_session.commit() - - yield +@pytest.fixture +def save(): with app.app_context(): - User_affiliations.query.delete() - Organizations.query.delete() - Users.query.delete() - db_session.commit() - - -@pytest.mark.usefixtures("org_perm_test_db_init") -class TestOrgResolverWithOrgs(TestCase): - # Super Admin Tests - def test_get_org_resolvers_by_org_super_admin_single_node(self): - """ - Test org resolver by organization as a super admin, single node return + yield s + cleanup() + + +def test_get_org_resolvers_by_org_super_admin_single_node(save): + """ + Test org resolver by organization as a super admin, single node return + """ + org1 = Organizations( + name="Org1", acronym="ORG1", org_tags={"name": "Organization 1"} + ) + org2 = Organizations( + name="Org2", acronym="ORG2", org_tags={"name": "Organization 2"} + ) + org3 = Organizations( + name="Org3", acronym="ORG3", org_tags={"name": "Organization 3"} + ) + + reader = Users( + display_name="testuserread", + user_name="testuserread@testemail.ca", + password="testpassword123", + ) + super_admin = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + ) + + super_admin.user_affiliation.append( + User_affiliations(permission="super_admin", user_organization=org1) + ) + + reader.user_affiliation.append( + User_affiliations(permission="user_read", user_organization=org1) + ) + + save(reader) + save(super_admin) + + token = tokenize(user_id=super_admin.id, roles=super_admin.roles) + + result = Client(schema).execute( """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken + { + organization(org: "ORG1") { + edges { + node { + acronym } } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - { - organization(org: "ORG1") { - edges { - node { - acronym - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - result_refr = { - "data": {"organization": {"edges": [{"node": {"acronym": "ORG1"}}]}} } - self.assertDictEqual(result_refr, executed) - - def test_get_org_resolvers_super_admin_multi_node(self): + } + """, + context_value=auth_header(token), + ) + assert result == { + "data": {"organization": {"edges": [{"node": {"acronym": "ORG1"}}]}} + } + + +def test_org_resolvers_returns_all_orgs_to_super_admin(save): + """ + Test organization resolver as a super admin, multi node return + """ + org1 = Organizations(name="Org1", acronym="ORG1") + + reader = Users( + display_name="testuserread", + user_name="testuserread@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations(permission="user_read", user_organization=org1) + ], + ) + super_admin = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations(permission="super_admin", user_organization=org1) + ], + ) + + save(reader) + save(super_admin) + + token = tokenize(user_id=super_admin.id, roles=super_admin.roles) + + result = Client(schema).execute( """ - Test organization resolver as a super admin, multi node return - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - { - organizations { - edges { - node { - acronym - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - result_refr = { - "data": { - "organizations": { - "edges": [ - {"node": {"acronym": "ORG1"}}, - {"node": {"acronym": "ORG2"}}, - {"node": {"acronym": "ORG3"}}, - ] + { + organizations { + edges { + node { + name } } } - self.assertDictEqual(result_refr, executed) - - # User read tests - def test_get_org_resolvers_by_org_user_read_single_node(self): - """ - Test orgnization resolver by org as user read, return as - single node - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - { - organization(org: "ORG1") { - edges { - node { - acronym - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - result_refr = { - "data": {"organization": {"edges": [{"node": {"acronym": "ORG1"}}]}} + } + """, + context_value=auth_header(token), + ) + assert result == { + "data": { + "organizations": { + "edges": [ + {"node": {"name": "Org1"}}, + {"node": {"name": "testsuperadmin@testemail.ca"}}, + {"node": {"name": "testuserread@testemail.ca"}}, + ] } - self.assertDictEqual(result_refr, executed) - - def test_get_org_resolvers_by_org_user_read_multi_node(self): + } + } + + +def test_org_resolvers_returns_single_org1_and_users_own_org_for_read_users(save): + org1 = Organizations(name="Org1", acronym="ORG1") + org2 = Organizations(name="SA", acronym="SA") + + reader = Users( + display_name="testuserread", + user_name="testuserread@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations(permission="user_read", user_organization=org1) + ], + ) + super_admin = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations(permission="super_admin", user_organization=org1), + User_affiliations(permission="super_admin", user_organization=org2), + ], + ) + + save(reader) + save(super_admin) + + token = tokenize(user_id=reader.id, roles=reader.roles) + + result = Client(schema).execute( """ - Test domain resolver get domain by org as user read, return as - multi node - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken + { + organizations { + edges { + node { + name } } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - { - organizations { - edges { - node { - acronym - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - result_refr = { - "data": {"organizations": {"edges": [{"node": {"acronym": "ORG1"}}]}} } - self.assertDictEqual(result_refr, executed) - - def test_get_org_resolvers_by_org_user_read_no_access(self): - """ - Test domain resolver get domain by url as user read, user has no rights - to view domains related to that org - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - { - organization(org: "ORG2") { - edges { - node { - acronym - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you do not have permission to view that organization" - ) - - -@pytest.fixture(scope="class") -def org_no_perm_test_db_init(): - - with app.app_context(): - test_super_admin = Users( - id=2, - display_name="testsuperadmin", - user_name="testsuperadmin@testemail.ca", - password="testpassword123", - ) - db_session.add(test_super_admin) - test_admin_role = User_affiliations(user_id=2, permission="super_admin") - db_session.add(test_admin_role) - db_session.commit() + } + """, + context_value=auth_header(token), + ) + assert result == { + "data": { + "organizations": { + "edges": [ + {"node": {"name": "Org1"}}, + {"node": {"name": "testuserread@testemail.ca"}}, + ] + } + } + } - yield - with app.app_context(): - User_affiliations.query.delete() - Users.query.delete() - db_session.commit() +def test_org_resolvers_does_not_show_orgs_reader_is_not_affiliated_with(save): + org1 = Organizations(name="Org1", acronym="ORG1") + org2 = Organizations(name="Org2", acronym="ORG2") + reader = Users( + display_name="testuserread", + user_name="testuserread@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations(permission="user_read", user_organization=org1) + ], + ) -@pytest.mark.usefixtures("org_no_perm_test_db_init") -class TestOrgResolverWithoutOrgs(TestCase): - def test_get_org_resolvers_super_admin_no_orgs(self): - """ - Test org resolver by organization as a super admin, no orgs exist - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None + save(reader) - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) + token = tokenize(user_id=reader.id, roles=reader.roles) - executed = client.execute( - """ - { - organizations { - edges { - node { - acronym - } - } - } - } - """, - context_value=request_headers, - backend=backend, - ) - assert executed["errors"] - assert executed["errors"][0] - assert executed["errors"][0]["message"] == "Error, no organizations to view" - - def test_get_org_resolvers_by_org_super_admin_no_orgs(self): - """ - Test org resolver by organization as a super admin, no orgs exist + result = Client(schema).execute( """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) - - executed = client.execute( - """ - { - organization(org: "ORG3"){ - edges { - node { - acronym - } - } + { + organization(org: "ORG2") { + edges { + node { + name } } - """, - context_value=request_headers, - backend=backend, - ) - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] == "Error, organization does not exist" + } + } + """, + context_value=auth_header(token), + ) + if "errors" not in result: + fail( + "Expected read user request for non-affiliated org to error. Instead: {}".format( + result ) + ) + [err] = result["errors"] + assert err["message"] == "Error, organization does not exist" diff --git a/api/tests/test_organization_resolver_values.py b/api/tests/test_organization_resolver_values.py index b228a97595..5275a4ec0f 100644 --- a/api/tests/test_organization_resolver_values.py +++ b/api/tests/test_organization_resolver_values.py @@ -1,523 +1,586 @@ import pytest +import json +from pytest import fail from flask import Request from graphene.test import Client from unittest import TestCase from werkzeug.test import create_environ from app import app -from db import db_session +from db import DB from models import Organizations, Domains, Users, User_affiliations from queries import schema from backend.security_check import SecurityAnalysisBackend +save, cleanup, db_session = DB() + @pytest.fixture(scope="class") def org_perm_test_db_init(): with app.app_context(): - test_user = Users( - id=1, - display_name="testuserread", - user_name="testuserread@testemail.ca", - password="testpassword123", - ) - db_session.add(test_user) - test_super_admin = Users( - id=2, - display_name="testsuperadmin", - user_name="testsuperadmin@testemail.ca", - password="testpassword123", - ) - db_session.add(test_super_admin) - - org = Organizations( - id=1, + org1 = Organizations( acronym="ORG1", + domains=[Domains(domain="somecooldomain.ca")], org_tags={ - "description": "Organization 1", + "name": "Organization 1", "zone": "Prov", "sector": "Banking", "province": "Alberta", "city": "Calgary", }, ) - db_session.add(org) - org = Organizations( - id=2, + save(org1) + org2 = Organizations( acronym="ORG2", + domains=[Domains(domain="anothercooldomain.ca")], org_tags={ - "description": "Organization 2", + "name": "Organization 2", "zone": "Muni", "sector": "Transportation", "province": "NS", "city": "Halifax", }, ) - db_session.add(org) - org = Organizations( - id=3, + save(org2) + org3 = Organizations( acronym="ORG3", + domains=[Domains(domain="somelamedomain.ca")], org_tags={ - "description": "Organization 3", + "name": "Organization 3", "zone": "Federal", "sector": "Arts", "province": "Ontario", "city": "Toronto", }, ) - db_session.add(org) - - test_admin_role = User_affiliations( - user_id=1, organization_id=1, permission="user_read" + save(org3) + user = Users( + display_name="testuserread", + user_name="testuserread@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations(user_organization=org1, permission="user_read") + ], ) - db_session.add(test_admin_role) - test_admin_role = User_affiliations( - user_id=2, organization_id=1, permission="super_admin" + super_admin = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + user_affiliation=[ + User_affiliations(user_organization=org1, permission="super_admin") + ], ) - db_session.add(test_admin_role) - domain = Domains(id=1, domain="somecooldomain.ca", organization_id=1) - db_session.add(domain) - domain = Domains(id=2, domain="anothercooldomain.ca", organization_id=2) - db_session.add(domain) - domain = Domains(id=3, domain="somelamedomain.ca", organization_id=3) - db_session.add(domain) - db_session.commit() + save(user) + save(super_admin) - yield + yield - with app.app_context(): - Domains.query.delete() - User_affiliations.query.delete() - Organizations.query.delete() - Users.query.delete() - db_session.commit() + cleanup() @pytest.mark.usefixtures("org_perm_test_db_init") -class TestOrgResolverWithOrgsAndValues(TestCase): - # Super Admin Tests - def test_get_org_resolvers_by_org_super_admin_single_node(self): - """ - Test org resolver by organization as a super admin, single node return - with all values - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } +# Super Admin Tests +def test_get_org_resolvers_by_org_super_admin_single_node(): + """ + Test org resolver by organization as a super admin, single node return + with all values + """ + with app.app_context(): + backend = SecurityAnalysisBackend() + client = Client(schema) + get_token = client.execute( + """ + mutation{ + signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ + authToken } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None + } + """, + backend=backend, + ) + assert get_token["data"]["signIn"]["authToken"] is not None + token = get_token["data"]["signIn"]["authToken"] + assert token is not None - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) + environ = create_environ() + environ.update(HTTP_AUTHORIZATION=token) + request_headers = Request(environ) - executed = client.execute( - """ - { - organization(org: "ORG1") { - edges { - node { - acronym - description - zone - sector - province - city - domains { - edges { - node { - url - } + actual = client.execute( + """ + { + organization(org: "ORG1") { + edges { + node { + acronym + name + zone + sector + province + city + domains { + edges { + node { + url } } - affiliatedUsers { - edges { - node { - user { - displayName - } - permission + } + affiliatedUsers { + edges { + node { + user { + displayName } + permission } } } } } } - """, - context_value=request_headers, - backend=backend, - ) - result_refr = { - "data": { - "organization": { - "edges": [ - { - "node": { - "acronym": "ORG1", - "description": "Organization 1", - "zone": "Prov", - "sector": "Banking", - "province": "Alberta", - "city": "Calgary", - "domains": { - "edges": [ - {"node": {"url": "somecooldomain.ca"}} - ] - }, - "affiliatedUsers": { - "edges": [ - { - "node": { - "user": { - "displayName": "testuserread" - }, - "permission": "USER_READ", - } - }, - { - "node": { - "user": { - "displayName": "testsuperadmin" - }, - "permission": "SUPER_ADMIN", - } - }, - ] - }, - } + } + """, + context_value=request_headers, + backend=backend, + ) + expected = { + "data": { + "organization": { + "edges": [ + { + "node": { + "acronym": "ORG1", + "name": None, + "zone": "Prov", + "sector": "Banking", + "province": "Alberta", + "city": "Calgary", + "domains": { + "edges": [{"node": {"url": "somecooldomain.ca"}}] + }, + "affiliatedUsers": { + "edges": [ + { + "node": { + "user": {"displayName": "testuserread"}, + "permission": "USER_READ", + } + }, + { + "node": { + "user": { + "displayName": "testsuperadmin" + }, + "permission": "SUPER_ADMIN", + } + }, + ] + }, } - ] - } + } + ] } } - self.assertDictEqual(result_refr, executed) + } - def test_get_org_resolvers_super_admin_multi_node(self): - """ - Test organization resolver as a super admin, multi node return with - all values - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ - authToken - } - } - """, - backend=backend, + if "errors" in actual: + fail( + "Expected super admin to return results for all users but got: {}".format( + result["errors"] + ) ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) + assert actual == expected - executed = client.execute( - """ - { - organizations { - edges { - node { - acronym - description - zone - sector - province - city - domains { - edges { - node { - url - } + +@pytest.mark.usefixtures("org_perm_test_db_init") +def test_get_org_resolvers_super_admin_multi_node(): + """ + Test organization resolver as a super admin, multi node return with + all values + """ + with app.app_context(): + backend = SecurityAnalysisBackend() + client = Client(schema) + get_token = client.execute( + """ + mutation{ + signIn(userName:"testsuperadmin@testemail.ca", password:"testpassword123"){ + authToken + } + } + """, + backend=backend, + ) + if "errors" in get_token: + print(get_token["errors"]) + assert get_token["data"]["signIn"]["authToken"] is not None + token = get_token["data"]["signIn"]["authToken"] + assert token is not None + + environ = create_environ() + environ.update(HTTP_AUTHORIZATION=token) + request_headers = Request(environ) + + actual = client.execute( + """ + { + organizations { + edges { + node { + acronym + name + zone + sector + province + city + domains { + edges { + node { + url } } - affiliatedUsers { - edges { - node { - user { - displayName - } - permission + } + affiliatedUsers { + edges { + node { + user { + displayName } + permission } } } } } } - """, - context_value=request_headers, - backend=backend, - ) - result_refr = { - "data": { - "organizations": { - "edges": [ - { - "node": { - "acronym": "ORG1", - "description": "Organization 1", - "zone": "Prov", - "sector": "Banking", - "province": "Alberta", - "city": "Calgary", - "domains": { - "edges": [ - {"node": {"url": "somecooldomain.ca"}} - ] - }, - "affiliatedUsers": { - "edges": [ - { - "node": { - "user": { - "displayName": "testuserread" - }, - "permission": "USER_READ", - } - }, - { - "node": { - "user": { - "displayName": "testsuperadmin" - }, - "permission": "SUPER_ADMIN", - } - }, - ] - }, - } - }, - { - "node": { - "acronym": "ORG2", - "description": "Organization 2", - "zone": "Muni", - "sector": "Transportation", - "province": "NS", - "city": "Halifax", - "domains": { - "edges": [ - {"node": {"url": "anothercooldomain.ca"}} - ] - }, - "affiliatedUsers": {"edges": []}, - } - }, - { - "node": { - "acronym": "ORG3", - "description": "Organization 3", - "zone": "Federal", - "sector": "Arts", - "province": "Ontario", - "city": "Toronto", - "domains": { - "edges": [ - {"node": {"url": "somelamedomain.ca"}} - ] - }, - "affiliatedUsers": {"edges": []}, - } - }, - ] - } - } } - self.assertDictEqual(result_refr, executed) + """, + context_value=request_headers, + backend=backend, + ) - # User read tests - def test_get_org_resolvers_by_org_user_read_single_node(self): - """ - Test org resolver with an org as a user read, multi node return with - all values - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } + expected = { + "data": { + "organizations": { + "edges": [ + { + "node": { + "acronym": "ORG1", + "name": None, + "zone": "Prov", + "sector": "Banking", + "province": "Alberta", + "city": "Calgary", + "domains": { + "edges": [{"node": {"url": "somecooldomain.ca"}}] + }, + "affiliatedUsers": { + "edges": [ + { + "node": { + "user": {"displayName": "testuserread"}, + "permission": "USER_READ", + } + }, + { + "node": { + "user": { + "displayName": "testsuperadmin" + }, + "permission": "SUPER_ADMIN", + } + }, + ] + }, + } + }, + { + "node": { + "acronym": "ORG2", + "name": None, + "zone": "Muni", + "sector": "Transportation", + "province": "NS", + "city": "Halifax", + "domains": { + "edges": [{"node": {"url": "anothercooldomain.ca"}}] + }, + "affiliatedUsers": {"edges": []}, + } + }, + { + "node": { + "acronym": "ORG3", + "name": None, + "zone": "Federal", + "sector": "Arts", + "province": "Ontario", + "city": "Toronto", + "domains": { + "edges": [{"node": {"url": "somelamedomain.ca"}}] + }, + "affiliatedUsers": {"edges": []}, + } + }, + { + "node": { + "acronym": "TESTUSERRE", + "name": "testuserread@testemail.ca", + "zone": None, + "sector": None, + "province": None, + "city": None, + "domains": {"edges": []}, + "affiliatedUsers": { + "edges": [ + { + "node": { + "user": {"displayName": "testuserread"}, + "permission": "ADMIN", + } + } + ] + }, + } + }, + { + "node": { + "acronym": "TESTSUPERA", + "name": "testsuperadmin@testemail.ca", + "zone": None, + "sector": None, + "province": None, + "city": None, + "domains": {"edges": []}, + "affiliatedUsers": { + "edges": [ + { + "node": { + "user": { + "displayName": "testsuperadmin" + }, + "permission": "ADMIN", + } + } + ] + }, + } + }, + ] } - """, - backend=backend, + } + } + + if "errors" in actual: + fail( + "Expected super admin to return results for all users but got: {}".format( + actual["errors"] + ) ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) + assert actual == expected - executed = client.execute( - """ - { - organization(org: "ORG1") { - edges { - node { - acronym - description - zone - sector - province - city - domains { - edges { - node { - url - } + +# User read tests +@pytest.mark.usefixtures("org_perm_test_db_init") +def test_get_org_resolvers_by_org_user_read_single_node(): + """ + Test org resolver with an org as a user read, multi node return with + all values + """ + with app.app_context(): + backend = SecurityAnalysisBackend() + client = Client(schema) + get_token = client.execute( + """ + mutation{ + signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ + authToken + } + } + """, + backend=backend, + ) + assert get_token["data"]["signIn"]["authToken"] is not None + token = get_token["data"]["signIn"]["authToken"] + assert token is not None + + environ = create_environ() + environ.update(HTTP_AUTHORIZATION=token) + request_headers = Request(environ) + + actual = client.execute( + """ + { + organization(org: "ORG1") { + edges { + node { + acronym + name + zone + sector + province + city + domains { + edges { + node { + url } } - affiliatedUsers { - edges { - node { - user { - displayName - } - permission + } + affiliatedUsers { + edges { + node { + user { + displayName } + permission } } } } } } - """, - context_value=request_headers, - backend=backend, - ) - result_refr = { - "data": { - "organization": { - "edges": [ - { - "node": { - "acronym": "ORG1", - "description": "Organization 1", - "zone": "Prov", - "sector": "Banking", - "province": "Alberta", - "city": "Calgary", - "domains": { - "edges": [ - {"node": {"url": "somecooldomain.ca"}} - ] - }, - "affiliatedUsers": {"edges": []}, - } + } + """, + context_value=request_headers, + backend=backend, + ) + expected = { + "data": { + "organization": { + "edges": [ + { + "node": { + "acronym": "ORG1", + "name": None, + "zone": "Prov", + "sector": "Banking", + "province": "Alberta", + "city": "Calgary", + "domains": { + "edges": [{"node": {"url": "somecooldomain.ca"}}] + }, + "affiliatedUsers": {"edges": []}, } - ] - } + } + ] } } - self.assertDictEqual(result_refr, executed) + } - def test_get_org_resolvers_by_org_user_read_multi_node(self): - """ - Test organizations resolver as a user read, multi node return with - all values - """ - with app.app_context(): - backend = SecurityAnalysisBackend() - client = Client(schema) - get_token = client.execute( - """ - mutation{ - signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ - authToken - } + assert actual == expected + + +@pytest.mark.usefixtures("org_perm_test_db_init") +def test_get_org_resolvers_by_org_user_read_multi_node(): + """ + Test organizations resolver as a user read, multi node return with + all values + """ + with app.app_context(): + backend = SecurityAnalysisBackend() + client = Client(schema) + get_token = client.execute( + """ + mutation{ + signIn(userName:"testuserread@testemail.ca", password:"testpassword123"){ + authToken } - """, - backend=backend, - ) - assert get_token["data"]["signIn"]["authToken"] is not None - token = get_token["data"]["signIn"]["authToken"] - assert token is not None + } + """, + backend=backend, + ) + assert get_token["data"]["signIn"]["authToken"] is not None + token = get_token["data"]["signIn"]["authToken"] + assert token is not None - environ = create_environ() - environ.update(HTTP_AUTHORIZATION=token) - request_headers = Request(environ) + environ = create_environ() + environ.update(HTTP_AUTHORIZATION=token) + request_headers = Request(environ) - executed = client.execute( - """ - { - organizations { - edges { - node { - acronym - description - zone - sector - province - city - domains { - edges { - node { - url - } + actual = client.execute( + """ + { + organizations { + edges { + node { + acronym + name + zone + sector + province + city + domains { + edges { + node { + url } } - affiliatedUsers { - edges { - node { - user { - displayName - } - permission + } + affiliatedUsers { + edges { + node { + user { + displayName } + permission } } } } } } - """, - context_value=request_headers, - backend=backend, - ) - result_refr = { - "data": { - "organizations": { - "edges": [ - { - "node": { - "acronym": "ORG1", - "description": "Organization 1", - "zone": "Prov", - "sector": "Banking", - "province": "Alberta", - "city": "Calgary", - "domains": { - "edges": [ - {"node": {"url": "somecooldomain.ca"}} - ] - }, - "affiliatedUsers": {"edges": []}, - } + } + """, + context_value=request_headers, + backend=backend, + ) + expected = { + "data": { + "organizations": { + "edges": [ + { + "node": { + "acronym": "ORG1", + "name": None, + "zone": "Prov", + "sector": "Banking", + "province": "Alberta", + "city": "Calgary", + "domains": { + "edges": [{"node": {"url": "somecooldomain.ca"}}] + }, + "affiliatedUsers": {"edges": []}, } - ] - } + }, + { + "node": { + "acronym": "TESTUSERRE", + "name": "testuserread@testemail.ca", + "zone": None, + "sector": None, + "province": None, + "city": None, + "domains": {"edges": []}, + "affiliatedUsers": { + "edges": [ + { + "node": { + "user": {"displayName": "testuserread"}, + "permission": "ADMIN", + } + } + ] + }, + } + }, + ] } } - self.assertDictEqual(result_refr, executed) + } + + if "errors" in actual: + fail("Expect success but errors were returned: {}".format(result["errors"])) + assert actual == expected diff --git a/api/tests/test_organizations.py b/api/tests/test_organizations.py new file mode 100644 index 0000000000..8247e25b3c --- /dev/null +++ b/api/tests/test_organizations.py @@ -0,0 +1,17 @@ +import pytest +from app import app +from models import Organizations +from db import DB + +s, cleanup, _ = DB() + +@pytest.fixture +def save(): + with app.app_context(): + yield s + cleanup() + + +def test_orgs_make_a_slug_from_the_name(): + org = Organizations(name="Treasury Board Secretariat") + assert org.slug == "treasury-board-secretariat" diff --git a/api/tests/test_removeOrganization_mutation.py b/api/tests/test_removeOrganization_mutation.py new file mode 100644 index 0000000000..83ccf37857 --- /dev/null +++ b/api/tests/test_removeOrganization_mutation.py @@ -0,0 +1,290 @@ +import pytest +from json_web_token import tokenize +from flask import Request +from pytest import fail +from graphene.test import Client +from unittest import TestCase +from werkzeug.test import create_environ +from app import app +from db import DB +from queries import schema +from backend.security_check import SecurityAnalysisBackend +from models import ( + Organizations, + Domains, + Users, + User_affiliations, + Scans, + Dkim_scans, + Dmarc_scans, + Https_scans, + Mx_scans, + Spf_scans, + Ssl_scans, +) + +s, cleanup, session = DB() + +def auth_header(token): + env = create_environ() + env.update(HTTP_AUTHORIZATION=token) + return Request(env) + + +@pytest.fixture +def save(): + with app.app_context(): + yield s + cleanup() + + +def test_mutation_removeOrganization_succeeds_for_super_admin(save): + sa_user = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + ) + sa_user.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations(acronym="SA", name="Super Admin"), + ) + ) + sa_user.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations(acronym="ORG1", name="Org One"), + ) + ) + + save(sa_user) + + token = tokenize(user_id=sa_user.id, roles=sa_user.roles) + + result = Client(schema).execute( + """ + mutation { + removed:removeOrganization(acronym: "ORG1") { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" in result: + fail( + "expected removeOrganization to succeed for SA user. Instead: {}".format( + result + ) + ) + + created_org = result["data"].values() + [status] = created_org + + assert status == {"status": True} + + +def test_mutation_removeOrganization_does_not_remove_super_admin_org(save): + sa_user = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + ) + sa_user.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations(acronym="SA", name="Super Admin"), + ) + ) + + save(sa_user) + + token = tokenize(user_id=sa_user.id, roles=sa_user.roles) + + result = Client(schema).execute( + """ + mutation { + removed:removeOrganization(acronym: "SA") { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected removing the SA org to fail, even for SA user. Instead: {}".format( + result + ) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, you cannot remove this organization" + + +def test_mutation_removeOrganization_fails_if_org_does_not_exist(save): + sa_user = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + ) + sa_user.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations(acronym="SA", name="Super Admin"), + ) + ) + + save(sa_user) + + token = tokenize(user_id=sa_user.id, roles=sa_user.roles) + + result = Client(schema).execute( + """ + mutation { + removeOrganization( + acronym: "RANDOM" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected removeOrganization to fail for orgs that don't exist. Instead: {}".format( + result + ) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, organization does not exist" + + +def test_mutation_removeOrganization_fails_for_admin_users(save): + admin = Users( + display_name="admin", user_name="admin@example.com", password="testpassword123", + ) + admin.user_affiliation.append( + User_affiliations( + permission="admin", + user_organization=Organizations(acronym="ORG1", name="Org One",), + ) + ) + + save(admin) + + token = tokenize(user_id=admin.id, roles=admin.roles) + + result = Client(schema).execute( + """ + mutation { + removeOrganization( + acronym: "ORG1" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected removeOrganization to fail for admins. Instead: {}".format(result) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, you do not have permission to remove organizations." + + +def test_mutation_removeOrganization_fails_for_write_users(save): + write_user = Users( + display_name="writer", user_name="write_user@example.com", password="testpassword123", + ) + + write_user.user_affiliation.append( + User_affiliations( + permission="user_write", + user_organization=Organizations(acronym="ORG1", name="Org One"), + ) + ) + + save(write_user) + + token = tokenize(user_id=write_user.id, roles=write_user.roles) + + result = Client(schema).execute( + """ + mutation { + removeOrganization( + acronym: "ORG1" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected removeOrganization to fail for write users. Instead: {}".format(result) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, you do not have permission to remove organizations." + + +def test_mutation_removeOrganization_fails_for_read_users(save): + reader = Users( + display_name="reader", + user_name="reader@example.com", + password="testpassword123", + ) + + reader.user_affiliation.append( + User_affiliations( + permission="user_read", + user_organization=Organizations(acronym="ORG1", name="Org One"), + ) + ) + + save(reader) + + token = tokenize(user_id=reader.id, roles=reader.roles) + + result = Client(schema).execute( + """ + mutation { + removeOrganization( + acronym: "ORG1" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected removeOrganization to fail for read users. Instead: {}".format(result) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, you do not have permission to remove organizations." diff --git a/api/tests/test_updateOrganization_mutation.py b/api/tests/test_updateOrganization_mutation.py new file mode 100644 index 0000000000..bb64594348 --- /dev/null +++ b/api/tests/test_updateOrganization_mutation.py @@ -0,0 +1,338 @@ +import pytest +from json_web_token import tokenize, auth_header +from flask import Request +from pytest import fail +from graphene.test import Client +from unittest import TestCase +from werkzeug.test import create_environ +from app import app +from db import DB +from queries import schema +from backend.security_check import SecurityAnalysisBackend +from models import ( + Organizations, + Domains, + Users, + User_affiliations, + Scans, + Dkim_scans, + Dmarc_scans, + Https_scans, + Mx_scans, + Spf_scans, + Ssl_scans, +) + +s, cleanup, session = DB() + + +@pytest.fixture +def save(): + with app.app_context(): + yield s + cleanup() + + +def test_mutation_updateOrganization_succeeds_as_super_user(save): + sa_user = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + ) + sa_user.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations(acronym="SA", name="Super Admin"), + ) + ) + sa_user.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations(acronym="ORG1", name="Org One"), + ) + ) + + save(sa_user) + + token = tokenize(user_id=sa_user.id, roles=sa_user.roles) + + result = Client(schema).execute( + """ + mutation { + updateOrganization( + name: "Org One" + acronym: "ORG1" + updatedAcronym: "O1" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" in result: + fail( + "expected updateOrganization to succeed for super admin. Instead: {}".format( + result + ) + ) + + created_org = result["data"].values() + [status] = created_org + + assert status == {"status": True} + + +def test_mutation_updateOrganization_fails_if_names_clash(save): + sa_user = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + ) + sa_user.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations(acronym="SA", name="Super Admin"), + ) + ) + sa_user.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations(acronym="ORG1", name="Org One"), + ) + ) + + save(sa_user) + + token = tokenize(user_id=sa_user.id, roles=sa_user.roles) + + result = Client(schema).execute( + """ + mutation { + updateOrganization( + name: "Org One" + acronym: "ORG1" + updatedAcronym: "SA" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected updateOrganization to fail when renaming clashes. Instead: {}".format( + result + ) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, acronym already in use." + + +def test_mutation_updateOrganization_fails_if_org_does_not_exist(save): + sa_user = Users( + display_name="testsuperadmin", + user_name="testsuperadmin@testemail.ca", + password="testpassword123", + ) + sa_user.user_affiliation.append( + User_affiliations( + permission="super_admin", + user_organization=Organizations(acronym="SA", name="Super Admin"), + ) + ) + + save(sa_user) + + token = tokenize(user_id=sa_user.id, roles=sa_user.roles) + + result = Client(schema).execute( + """ + mutation { + updateOrganization( + name: "Org One" + acronym: "ORG1" + updatedAcronym: "O1" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected updateOrganization to fail for orgs that don't exist. Instead: {}".format( + result + ) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, organization does not exist." + + +def test_mutation_updateOrganization_fails_for_admin_users(save): + admin = Users( + display_name="admin", user_name="admin@example.com", password="testpassword123", + ) + + admin.user_affiliation.append( + User_affiliations( + permission="admin", + user_organization=Organizations(acronym="ORG1", name="Org One"), + ) + ) + + save(admin) + + token = tokenize(user_id=admin.id, roles=admin.roles) + + result = Client(schema).execute( + """ + mutation { + updateOrganization( + acronym: "ORG1" + updatedAcronym: "O1" + name: "Test Organization" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected createOrganization to fail for admins. Instead: {}".format(result) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, you do not have permission to update organizations" + + +def test_mutation_updateOrganization_fails_for_write_users(save): + write_user = Users( + display_name="writer", + user_name="write_user@example.com", + password="testpassword123", + ) + + write_user.user_affiliation.append( + User_affiliations( + permission="user_write", + user_organization=Organizations(acronym="ORG1", name="Org One"), + ) + ) + + save(write_user) + + token = tokenize(user_id=write_user.id, roles=write_user.roles) + + result = Client(schema).execute( + """ + mutation { + updateOrganization( + acronym: "ORG1" + updatedAcronym: "O1" + name: "Test Organization" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected createOrganization to fail for write users. Instead: {}".format( + result + ) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, you do not have permission to update organizations" + + +def test_mutation_updateOrganization_fails_for_read_users(save): + reader = Users( + display_name="reader", + user_name="reader@example.com", + password="testpassword123", + ) + + reader.user_affiliation.append( + User_affiliations( + permission="user_read", + user_organization=Organizations(acronym="ORG1", name="Org One"), + ) + ) + + save(reader) + + token = tokenize(user_id=reader.id, roles=reader.roles) + + result = Client(schema).execute( + """ + mutation { + updateOrganization( + acronym: "ORG1" + updatedAcronym: "O1" + name: "Test Organization" + zone: "Test Zone" + sector: "Test Sector" + province: "Nova Scotia" + city: "Halifax" + ) { + status + } + } + """, + context_value=auth_header(token), + ) + + if "errors" not in result: + fail( + "expected createOrganization to fail for read users. Instead: {}".format( + result + ) + ) + + errors, data = result.values() + [first] = errors + message, _, _ = first.values() + assert message == "Error, you do not have permission to update organizations" diff --git a/api/tests/test_user_access_control.py b/api/tests/test_user_access_control.py index 634b02031d..481d79d95f 100644 --- a/api/tests/test_user_access_control.py +++ b/api/tests/test_user_access_control.py @@ -1,14 +1,17 @@ import pytest +from pytest import fail from flask import Request from graphene.test import Client from unittest import TestCase from werkzeug.test import create_environ from app import app -from db import db_session +from db import DB from models import Organizations, Users, User_affiliations from queries import schema from backend.security_check import SecurityAnalysisBackend +_, cleanup, db_session = DB() + @pytest.fixture(scope="class") def user_resolver_ac_test_db_init(): @@ -78,13 +81,8 @@ def user_resolver_ac_test_db_init(): db_session.add(test_user_write_role) db_session.commit() - yield - - with app.app_context(): - User_affiliations.query.delete() - Organizations.query.delete() - Users.query.delete() - db_session.commit() + yield + cleanup() @pytest.mark.usefixtures("user_resolver_ac_test_db_init") @@ -263,7 +261,7 @@ def test_get_user_user_write(self): ) # User read tests - def test_get_user_user_read(self): + def test_get_reader_can_access_their_own_info(self): """ Test to see if user resolver access control to ensure users with user write access cannot access this query @@ -289,7 +287,7 @@ def test_get_user_user_read(self): environ.update(HTTP_AUTHORIZATION=token) request_headers = Request(environ) - executed = client.execute( + results = client.execute( """ { user(userName: "testuserread@testemail.ca") { @@ -300,9 +298,4 @@ def test_get_user_user_read(self): context_value=request_headers, backend=backend, ) - assert executed["errors"] - assert executed["errors"][0] - assert ( - executed["errors"][0]["message"] - == "Error, you do not have permission to view this users information" - ) + assert results == {"data": {"user": [{"displayName": "testuserread"}]}} diff --git a/api/tests/test_user_mutations.py b/api/tests/test_user_mutations.py index 4d72fe5b3e..32109cdc4b 100644 --- a/api/tests/test_user_mutations.py +++ b/api/tests/test_user_mutations.py @@ -2,12 +2,13 @@ import pytest from graphene.test import Client from app import app -from db import db_session +from db import DB from queries import schema from models import Users from backend.security_check import SecurityAnalysisBackend from functions.error_messages import * +_, cleanup, db_session = DB() @pytest.fixture(scope="class") def user_schema_test_db_init(): @@ -26,11 +27,9 @@ def user_schema_test_db_init(): db_session.add(test_admin) db_session.commit() - yield + yield + cleanup() - with app.app_context(): - Users.query.delete() - db_session.commit() ## diff --git a/api/tests/test_user_values.py b/api/tests/test_user_values.py index e58a29a645..cbf2bc9ed2 100644 --- a/api/tests/test_user_values.py +++ b/api/tests/test_user_values.py @@ -4,58 +4,48 @@ from unittest import TestCase from werkzeug.test import create_environ from app import app -from db import db_session +from db import DB from models import Organizations, Users, User_affiliations from queries import schema from backend.security_check import SecurityAnalysisBackend +save, cleanup, session = DB() @pytest.fixture(scope="class") def user_resolver_test_db_init(): with app.app_context(): + org1 = Organizations(acronym="ORG1") + test_user = Users( - id=1, display_name="testuserread", user_name="testuserread@testemail.ca", password="testpassword123", preferred_lang="English", tfa_validated=False, + user_affiliation=[ + User_affiliations( + user_organization=org1, permission="user_read" + ) + ] ) - db_session.add(test_user) + save(test_user) + session.add(test_user) test_super_admin = Users( - id=2, display_name="testsuperadmin", user_name="testsuperadmin@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations( + user_organization=org1, permission="super_admin" + ) + ] ) - db_session.add(test_super_admin) - - org = Organizations(id=1, acronym="ORG1") - db_session.add(org) - org = Organizations(id=2, acronym="ORG2") - db_session.add(org) - org = Organizations(id=3, acronym="ORG3") - db_session.add(org) - - test_admin_role = User_affiliations( - user_id=1, organization_id=1, permission="user_read" - ) - db_session.add(test_admin_role) - test_admin_role = User_affiliations( - user_id=2, organization_id=1, permission="super_admin" - ) - db_session.add(test_admin_role) - - db_session.commit() + save(test_super_admin) - yield + yield + cleanup() - with app.app_context(): - User_affiliations.query.delete() - Organizations.query.delete() - Users.query.delete() - db_session.commit() @pytest.mark.usefixtures("user_resolver_test_db_init") @@ -86,7 +76,7 @@ def test_get_another_users_information(self): environ.update(HTTP_AUTHORIZATION=token) request_headers = Request(environ) - executed = client.execute( + results = client.execute( """ { user(userName: "testuserread@testemail.ca") { @@ -94,20 +84,13 @@ def test_get_another_users_information(self): displayName lang tfa - affiliations { - edges { - node { - userId - } - } - } } } """, context_value=request_headers, backend=backend, ) - result_refr = { + expected = { "data": { "user": [ { @@ -115,12 +98,11 @@ def test_get_another_users_information(self): "displayName": "testuserread", "lang": "English", "tfa": False, - "affiliations": {"edges": [{"node": {"userId": 1}}]}, } ] } } - self.assertDictEqual(result_refr, executed) + self.assertDictEqual(expected, results) def test_get_another_users_information_user_does_not_exist(self): """ @@ -152,16 +134,6 @@ def test_get_another_users_information_user_does_not_exist(self): { user(userName: "IdontThinkSo@testemail.ca") { userName - displayName - lang - tfa - affiliations { - edges { - node { - userId - } - } - } } } """, @@ -206,13 +178,6 @@ def test_get_own_user_information(self): displayName lang tfa - affiliations { - edges { - node { - userId - } - } - } } } """, @@ -227,7 +192,6 @@ def test_get_own_user_information(self): "displayName": "testuserread", "lang": "English", "tfa": False, - "affiliations": {"edges": [{"node": {"userId": 1}}]}, } ] } diff --git a/api/tests/test_users.py b/api/tests/test_users.py index 8edb0887a2..9b4c0d2557 100644 --- a/api/tests/test_users.py +++ b/api/tests/test_users.py @@ -3,7 +3,7 @@ from models import Users, User_affiliations, Organizations from db import DB -s, cleanup, _ = DB() +s, cleanup, session = DB() @pytest.fixture @@ -43,6 +43,20 @@ def test_user_model_encrypts_the_user_password(): assert len(user.password) is 60 +def test_user_is_admin_on_their_default_org(): + acceptable_password = "twelvechars!" + user = Users( + user_name="foo", + display_name="Foo", + preferred_lang="English", + password=acceptable_password, + ) + + for affiliation in user.user_affiliation: + assert affiliation.permission is "admin" + assert affiliation.user_organization.acronym == "FOO" + + def test_users_roles_can_be_accessed_by_a_roles_method(save): acceptable_password = "twelvechars!" user = Users( @@ -62,5 +76,5 @@ def test_users_roles_can_be_accessed_by_a_roles_method(save): ) # Before save org_id and user_id are None - role = filter(lambda d: d["permission"] == "user_write", user.roles) - assert user.roles == [{"org_id": None, "permission": "user_write", "user_id": None}] + role = [r for r in user.roles if r['permission'] == "user_write"] + assert role == [{"org_id": None, "permission": "user_write", "user_id": None}] diff --git a/api/tests/test_users_access_control.py b/api/tests/test_users_access_control.py index 2b899c5711..38c5a18cf2 100644 --- a/api/tests/test_users_access_control.py +++ b/api/tests/test_users_access_control.py @@ -1,90 +1,76 @@ import pytest from flask import Request +from json_web_token import tokenize, auth_header from graphene.test import Client from unittest import TestCase from werkzeug.test import create_environ from app import app -from db import db_session +from db import DB from models import Organizations, Users, User_affiliations from queries import schema from backend.security_check import SecurityAnalysisBackend +save, cleanup, db_session = DB() + + @pytest.fixture(scope="class") def users_resolver_test_db_init(): with app.app_context(): - test_read = Users( - id=1, + org1 = Organizations(acronym="ORG1") + org2 = Organizations(acronym="ORG2") + org3 = Organizations(acronym="ORG3") + + reader = Users( display_name="testuserread", user_name="testuserread@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations(user_organization=org1, permission="user_read") + ] ) - db_session.add(test_read) - test_super_admin = Users( - id=2, + super_admin = Users( display_name="testsuperadmin", user_name="testsuperadmin@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations(user_organization=org2, permission="super_admin") + ] ) - db_session.add(test_super_admin) - test_admin = Users( - id=3, + org1_admin = Users( display_name="testadmin", user_name="testadmin@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations(user_organization=org1, permission="admin") + ] ) - db_session.add(test_admin) - test_admin = Users( - id=4, + org2_admin = Users( display_name="testadmin2", user_name="testadmin2@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations(user_organization=org2, permission="admin") + ] ) - db_session.add(test_admin) - test_write = Users( - id=5, + writer = Users( display_name="testuserwrite", user_name="testuserwrite@testemail.ca", password="testpassword123", + user_affiliation=[ + User_affiliations(user_organization=org1, permission="user_write") + ] ) - db_session.add(test_write) + save(reader) + save(super_admin) + save(org1_admin) + save(org2_admin) + save(writer) - org = Organizations(id=1, acronym="ORG1") - db_session.add(org) - org = Organizations(id=2, acronym="ORG2") - db_session.add(org) - org = Organizations(id=3, acronym="ORG3") - db_session.add(org) - - test_user_read_role = User_affiliations( - user_id=1, organization_id=1, permission="user_read" - ) - db_session.add(test_user_read_role) - test_super_admin_role = User_affiliations( - user_id=2, organization_id=2, permission="super_admin" - ) - db_session.add(test_super_admin_role) - test_admin_role = User_affiliations( - user_id=3, organization_id=1, permission="admin" - ) - db_session.add(test_admin_role) - test_admin_role = User_affiliations( - user_id=4, organization_id=2, permission="admin" - ) - db_session.add(test_admin_role) - test_user_write_role = User_affiliations( - user_id=5, organization_id=1, permission="user_write" - ) - db_session.add(test_user_write_role) - db_session.commit() - yield + yield + cleanup() - with app.app_context(): - User_affiliations.query.delete() - Organizations.query.delete() - Users.query.delete() - db_session.commit() @pytest.mark.usefixtures("users_resolver_test_db_init") diff --git a/api/tests/testdata/__init__.py b/api/tests/testdata/__init__.py new file mode 100644 index 0000000000..da1b4f94ab --- /dev/null +++ b/api/tests/testdata/__init__.py @@ -0,0 +1,56 @@ +accurateplastics_report = { + "xml_schema": "draft", + "report_metadata": { + "org_name": "accurateplastics.com", + "org_email": "administrator@accurateplastics.com", + "org_extra_contact_info": "null", + "report_id": "example.com:1538463741", + "begin_date": "2018-10-01 13:07:12", + "end_date": "2018-10-01 13:07:12", + "errors": ["Invalid XML: not well-formed (invalid token): line 5, column 17"], + }, + "policy_published": { + "domain": "example.com", + "adkim": "r", + "aspf": "r", + "p": "none", + "sp": "reject", + "pct": "100", + "fo": "0", + }, + "records": [ + { + "source": { + "ip_address": "12.20.127.122", + "country": "US", + "reverse_dns": "null", + "base_domain": "null", + }, + "count": 1, + "alignment": {"spf": False, "dkim": False, "dmarc": False}, + "policy_evaluated": { + "disposition": "none", + "dkim": "fail", + "spf": "fail", + "policy_override_reasons": ["TESTING TEXT"], + }, + "identifiers": { + "header_from": "example.com", + "envelope_from": "null", + "envelope_to": "null", + }, + "auth_results": { + "dkim": [ + { + "domain": "toptierhighticket.club", + "selector": "default", + "result": "pass", + } + ], + "spf": [{"domain": "null", "scope": "mfrom", "result": "none"}], + }, + } + ], +} + +