Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ flask-migrate = "*"
pytest = "*"
pyotp = "*"
pyjwt = "*"
python-slugify = "*"

[scripts]
test = "python -m pytest"
Expand Down
45 changes: 33 additions & 12 deletions api/Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions api/functions/auth_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user_id is being pulled from the jwt, and the reason why there is another query to user_affiliations is to confirm that the users access is up to date and has not been modified since initial login

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we did a joinedload and loaded both the user and their affiliations in one shot at the beginning of the request that would be good. But I guess you're getting at the idea that another thread/query could change the state in the db between that inital load and when this gets executed. I'll have to think about that.

# 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)
Expand Down
4 changes: 3 additions & 1 deletion api/functions/sign_in_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ def sign_in_user(user_name, password):
}
user_roles.append(temp_dict)
else:
# XXX: Roles is [] || [""] || [{}]?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget the exact reason why its set like this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries! Little breadcrumbs leading us back to things to simplify.

# 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When this was initially created Po approved the 30min time limit

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha! We'll have to figure that out. I was finding that was so short that it was actually invalidating my jwts while I was debugging stuff!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I know that feeling 😂

"iat": datetime.datetime.utcnow(),
"user_id": user.id,
"roles": user_roles,
Expand Down
76 changes: 76 additions & 0 deletions api/migrations/versions/1860746a39e4_.py
Original file line number Diff line number Diff line change
@@ -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 ###
30 changes: 30 additions & 0 deletions api/migrations/versions/b781fb62a115_.py
Original file line number Diff line number Diff line change
@@ -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")
20 changes: 13 additions & 7 deletions api/models/Organizations.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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()
25 changes: 22 additions & 3 deletions api/models/User_affiliations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
)
21 changes: 20 additions & 1 deletion api/models/Users.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
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.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
from db import Base

Expand All @@ -20,9 +24,24 @@ 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 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="admin",
user_organization=Organizations(
name=self.user_name,
acronym=acronym,
)
)
)

@hybrid_method
def find_by_user_name(self, user_name):
return self.query.filter(self.user_name == user_name).first()
Expand Down
4 changes: 2 additions & 2 deletions api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down Expand Up @@ -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"))
Expand Down
Loading