Exemple #1
0
class FindingTemplateTranslation(Base, db.Model):
    lang = db.Column(Enum(Language), primary_key=True)
    finding_template_id = db.Column(
        db.Integer,
        db.ForeignKey('finding_template.id', onupdate='CASCADE', ondelete='CASCADE'),
        primary_key=True
    )
    finding_template = db.relationship(FindingTemplate, back_populates='translations', uselist=False)

    title = db.Column(db.String(128), nullable=False)
    definition = db.Column(db.String(), nullable=False)
    references = db.Column(db.String(), nullable=False)
    description = db.Column(db.String())

    def check_references_urls(self):
        url_regex = r"\[.+\]\((.+)\)"
        refs_lines = self.references.splitlines()

        for i, ref_line in enumerate(refs_lines):
            match_url = re.search(url_regex, ref_line)

            if match_url:
                ref = match_url.group(0)
                url = match_url.group(1)
                try:
                    req = requests.head(url, allow_redirects=True, timeout=config.BROKEN_REFS_REQ_TIMEOUT)
                    req.raise_for_status()

                    refs_lines[i] = ref_line.replace(config.BROKEN_REFS_TOKEN, "", 1)
                except:
                    if config.BROKEN_REFS_TOKEN not in ref_line:
                        refs_lines[i] = ref_line.replace(ref, f"{ref}{config.BROKEN_REFS_TOKEN}", 1)

        self.references = "\r\n".join(refs_lines)
        db.session.commit()
Exemple #2
0
class Active(Base, db.Model):
    __table_args__ = (db.UniqueConstraint('assessment_id', 'name'), )
    __serialization__ = [
        AttributeConfiguration(name='name',
                               csv_sequence=1,
                               **supported_serialization),
        AttributeConfiguration(name='uris', **supported_serialization),
    ]

    id = db.Column(db.Integer, primary_key=True)

    assessment_id = db.Column(db.Integer,
                              db.ForeignKey('assessment.id',
                                            onupdate='CASCADE',
                                            ondelete='CASCADE'),
                              nullable=False)
    assessment = db.relationship(Assessment, uselist=False)

    name = db.Column(db.String(128))

    active_resources = db.relationship('AffectedResource',
                                       back_populates='active')

    @property
    def uris(self):
        for resource in self.active_resources:
            yield resource.uri
Exemple #3
0
class Client(Base, db.Model):
    __serialization__ = [
        AttributeConfiguration(name='id',
                               csv_sequence=1,
                               **supported_serialization),
        AttributeConfiguration(name='short_name', **supported_serialization),
        AttributeConfiguration(name='long_name', **supported_serialization),
    ]

    id = db.Column(db.Integer, primary_key=True)
    short_name = db.Column(db.String(64), nullable=False)
    long_name = db.Column(db.String(128), nullable=False)

    assessments = db.relationship('Assessment', back_populates='client')
    templates = db.relationship('Template',
                                secondary=client_template,
                                back_populates='clients')

    creator_id = db.Column(db.Integer,
                           db.ForeignKey('user.id',
                                         onupdate="CASCADE",
                                         ondelete="CASCADE"),
                           nullable=False)
    creator = db.relationship("User",
                              back_populates="created_clients",
                              uselist=False)

    managers = db.relationship('User',
                               secondary=client_management,
                               back_populates='managed_clients')
    auditors = db.relationship('User',
                               secondary=client_audit,
                               back_populates='audited_clients')
Exemple #4
0
class AffectedResource(Base, db.Model):
    __table_args__ = (db.UniqueConstraint('active_id', 'route'), )
    __serialization__ = [
        AttributeConfiguration(name='uri',
                               csv_sequence=1,
                               **supported_serialization),
    ]

    id = db.Column(db.Integer, primary_key=True)

    active_id = db.Column(db.Integer,
                          db.ForeignKey('active.id',
                                        onupdate='CASCADE',
                                        ondelete='CASCADE'),
                          nullable=False)
    active = db.relationship(Active,
                             uselist=False,
                             back_populates='active_resources')

    route = db.Column(db.String(256))

    findings = db.relationship('Finding', secondary=finding_affected_resource)

    @property
    def uri(self):
        return "{}{}".format(self.active.name, self.route or '')

    def delete_last_reference(self):
        if len(self.findings) == 1:
            if len(self.active.active_resources
                   ) == 1 and self.active.active_resources[0] is self:
                self.active.delete()
            else:
                self.delete()
Exemple #5
0
class FindingTemplate(Base, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), nullable=False)
    type = db.Column(Enum(FindingType), nullable=False)

    owasp_category = db.Column(Enum(OWASPCategory))
    owasp_mobile_category = db.Column(Enum(OWASPMobileTop10Category))
    owisam_category = db.Column(Enum(OWISAMCategory))

    tech_risk = db.Column(Enum(Score), nullable=False)
    business_risk = db.Column(Enum(Score), nullable=False)
    exploitability = db.Column(Enum(Score), nullable=False)
    dissemination = db.Column(Enum(Score), nullable=False)
    solution_complexity = db.Column(Enum(Score), nullable=False)

    creator_id = db.Column(db.Integer,
                           db.ForeignKey('user.id',
                                         onupdate='CASCADE',
                                         ondelete='CASCADE'),
                           nullable=False)
    creator = db.relationship('User',
                              back_populates='created_findings',
                              uselist=False)

    solutions = db.relationship('Solution', back_populates='finding_template')
    translations = db.relationship('FindingTemplateTranslation',
                                   back_populates='finding_template')

    @property
    def langs(self):
        return {t.lang for t in self.translations}
Exemple #6
0
class Image(Base, db.Model):
    name = db.Column(db.String(128), primary_key=True)
    assessment_id = db.Column(
        db.Integer,
        db.ForeignKey('assessment.id', onupdate='CASCADE', ondelete='CASCADE'),
        primary_key=True
    )
    assessment = db.relationship(Assessment, back_populates='images', uselist=False)

    label = db.Column(db.String())
Exemple #7
0
class Solution(Base, db.Model):
    name = db.Column(db.String(32), primary_key=True)
    finding_template_id = db.Column(
        db.Integer,
        db.ForeignKey('finding_template.id', onupdate='CASCADE', ondelete='CASCADE'),
        primary_key=True
    )
    finding_template = db.relationship(FindingTemplate, back_populates='solutions', uselist=False)

    lang = db.Column(Enum(Language), nullable=False)
    text = db.Column(db.String(), nullable=False)
Exemple #8
0
class Client(Base, db.Model):
    __serialization__ = [
        AttributeConfiguration(name='id',
                               csv_sequence=1,
                               **supported_serialization),
        AttributeConfiguration(name='short_name', **supported_serialization),
        AttributeConfiguration(name='long_name', **supported_serialization),
    ]

    id = db.Column(db.Integer, primary_key=True)
    short_name = db.Column(db.String(64), nullable=False)
    long_name = db.Column(db.String(128), nullable=False)

    assessments = db.relationship('Assessment', back_populates='client')
    templates = db.relationship('Template',
                                secondary=client_template,
                                back_populates='clients')

    creator_id = db.Column(db.Integer,
                           db.ForeignKey('user.id',
                                         onupdate="CASCADE",
                                         ondelete="CASCADE"),
                           nullable=False)
    creator = db.relationship("User",
                              back_populates="created_clients",
                              uselist=False)

    managers = db.relationship('User',
                               secondary=client_management,
                               back_populates='managed_clients')
    auditors = db.relationship('User',
                               secondary=client_audit,
                               back_populates='audited_clients')

    finding_counter = db.Column(db.Integer, default=0, nullable=False)

    def generate_finding_counter(self) -> int:
        tx_commit = False
        while not tx_commit:
            self.finding_counter = Client.finding_counter + 1
            db.session.add(self)
            try:
                db.session.commit()
                tx_commit = True
            except Exception as ex:
                pass

        return self.finding_counter

    def format_finding_code(self, finding) -> str:
        client_name_prefix = unidecode(self.short_name).replace(" ",
                                                                "_").upper()

        return f"{client_name_prefix}_{finding.assessment.creation_date:%Y%m%d}_{finding.client_finding_id:06d}"
Exemple #9
0
class FindingTemplateTranslation(Base, db.Model):
    lang = db.Column(Enum(Language), primary_key=True)
    finding_template_id = db.Column(
        db.Integer,
        db.ForeignKey('finding_template.id', onupdate='CASCADE', ondelete='CASCADE'),
        primary_key=True
    )
    finding_template = db.relationship(FindingTemplate, back_populates='translations', uselist=False)

    title = db.Column(db.String(128), nullable=False)
    definition = db.Column(db.String(), nullable=False)
    references = db.Column(db.String(), nullable=False)
    description = db.Column(db.String())
Exemple #10
0
class FindingTemplate(Base, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), nullable=False)
    type = db.Column(Enum(FindingType), nullable=False)

    owasp_category = db.Column(Enum(OWASPCategory))
    owasp_mobile_category = db.Column(Enum(OWASPMobileTop10Category))
    owisam_category = db.Column(Enum(OWISAMCategory))

    tech_risk = db.Column(Enum(Score), nullable=False)
    business_risk = db.Column(Enum(Score), nullable=False)
    exploitability = db.Column(Enum(Score), nullable=False)
    dissemination = db.Column(Enum(Score), nullable=False)
    solution_complexity = db.Column(Enum(Score), nullable=False)

    creator_id = db.Column(db.Integer,
                           db.ForeignKey('user.id',
                                         onupdate='CASCADE',
                                         ondelete='CASCADE'),
                           nullable=False)
    creator = db.relationship('User',
                              back_populates='created_findings',
                              uselist=False)

    solutions = db.relationship('Solution', back_populates='finding_template')
    translations = db.relationship('FindingTemplateTranslation',
                                   back_populates='finding_template')

    cvss_v3_vector = db.Column(db.String(128))
    cvss_v3_score = db.Column(db.Float, default=0.0, nullable=False)

    @property
    def langs(self):
        return {t.lang for t in self.translations}

    @property
    def cvss_v3_severity(self):
        score = self.cvss_v3_score
        if score == 0:
            return Score.Info
        elif 0 < score < 4:
            return Score.Low
        elif 4 <= score < 7:
            return Score.Medium
        elif 7 <= score < 9:
            return Score.High
        else:
            return Score.Critical
Exemple #11
0
class Client(Base, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    short_name = db.Column(db.String(64), nullable=False)
    long_name = db.Column(db.String(128), nullable=False)

    assessments = db.relationship('Assessment', back_populates='client')
    templates = db.relationship('Template', backref='client')

    creator_id = db.Column(db.Integer, db.ForeignKey('user.id', onupdate="CASCADE", ondelete="CASCADE"), nullable=False)
    creator = db.relationship("User", back_populates="created_clients", uselist=False)

    managers = db.relationship('User', secondary=client_management, back_populates='managed_clients')
    auditors = db.relationship('User', secondary=client_audit, back_populates='audited_clients')

    def template_path(self):
        return os.path.join(config.TEMPLATES_PATH, str(self.id))
Exemple #12
0
class AffectedResource(Base, db.Model):
    __table_args__ = (db.UniqueConstraint('active_id', 'route'), )

    id = db.Column(db.Integer, primary_key=True)

    active_id = db.Column(db.Integer,
                          db.ForeignKey('active.id',
                                        onupdate='CASCADE',
                                        ondelete='CASCADE'),
                          nullable=False)
    active = db.relationship(Active,
                             uselist=False,
                             back_populates='active_resources')

    route = db.Column(db.String(256))

    findings = db.relationship('Finding', secondary=finding_affected_resource)

    @property
    def uri(self):
        return "{}{}".format(self.active.name, self.route or '')
Exemple #13
0
from sarna.model.base import Base, db, supported_serialization
from sarna.model.enums import Score, OWASPCategory, OWISAMCategory, FindingType, FindingStatus
from sarna.model.enums.category import OWASPMobileTop10Category
from sarna.model.finding_template import FindingTemplate, FindingTemplateTranslation
from sarna.model.sql_types import Enum

__all__ = [
    'Finding', 'Active', 'AffectedResource', 'finding_affected_resource'
]

finding_affected_resource = db.Table(
    'finding_affected_resource',
    db.Column('affected_resource_id',
              db.Integer,
              db.ForeignKey('affected_resource.id',
                            onupdate='CASCADE',
                            ondelete='CASCADE'),
              primary_key=True),
    db.Column('finding_id',
              db.Integer,
              db.ForeignKey('finding.id',
                            onupdate='CASCADE',
                            ondelete='CASCADE'),
              primary_key=True))


class Finding(Base, db.Model):
    __serialization__ = [
        AttributeConfiguration(name='id',
                               csv_sequence=1,
                               **supported_serialization),
Exemple #14
0
class Assessment(Base, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    uuid = db.Column(GUID, default=uuid4, unique=True, nullable=False)
    name = db.Column(db.String(64), nullable=False)
    platform = db.Column(db.String(64), nullable=False)
    lang = db.Column(Enum(Language), nullable=False)
    type = db.Column(Enum(AssessmentType), nullable=False)
    status = db.Column(Enum(AssessmentStatus), nullable=False)

    client_id = db.Column(
        db.Integer,
        db.ForeignKey('client.id', onupdate='CASCADE', ondelete='CASCADE'),
        nullable=False
    )
    client = db.relationship(Client, back_populates="assessments", uselist=False)

    findings = db.relationship('Finding', back_populates='assessment')
    actives = db.relationship('Active', back_populates='assessment')
    images = db.relationship('Image', back_populates='assessment')

    creation_date = db.Column(db.DateTime, default=lambda: datetime.now(), nullable=False)
    start_date = db.Column(db.Date)
    end_date = db.Column(db.Date)
    estimated_hours = db.Column(db.Integer)
    effective_hours = db.Column(db.Integer)

    approvals = db.relationship('User', secondary=auditor_approval, back_populates='approvals')

    creator_id = db.Column(db.Integer, db.ForeignKey('user.id', onupdate='CASCADE', ondelete='CASCADE'), nullable=False)
    creator = db.relationship("User", back_populates="created_assessments", uselist=False)

    auditors = db.relationship('User', secondary=assessment_audit, back_populates='audited_assessments')

    def _aggregate_score(self, field):
        counter = Counter(
            map(
                lambda x: getattr(x, field),
                self.findings
            )
        )

        return [
            counter[Score.Info],
            counter[Score.Low],
            counter[Score.Medium],
            counter[Score.High],
            counter[Score.Critical]
        ]

    def aggregate_finding_status(self):
        counter = Counter(
            map(
                lambda x: x.status,
                self.findings
            )
        )
        return [
            counter[FindingStatus.Pending],
            counter[FindingStatus.Reviewed],
            counter[FindingStatus.Confirmed],
            counter[FindingStatus.False_Positive],
            counter[FindingStatus.Other]
        ]

    def aggregate_technical_risk(self):
        return self._aggregate_score('tech_risk')

    def aggregate_business_risk(self):
        return self._aggregate_score('business_risk')

    def evidence_path(self):
        return os.path.join(config.EVIDENCES_PATH, str(self.uuid))
Exemple #15
0
from uuid import uuid4

from sarna.core.config import config
from sarna.model.base import Base, db
from sarna.model.client import Client
from sarna.model.enums import Language, AssessmentType, AssessmentStatus, Score, FindingStatus
from sarna.model.sql_types import Enum, GUID

__all__ = ['Assessment', 'Image', 'auditor_approval', 'assessment_audit']

auditor_approval = db.Table(
    'auditor_approval',
    db.Column(
        'approving_user_id',
        db.Integer,
        db.ForeignKey('user.id', onupdate='CASCADE', ondelete='CASCADE'),
        primary_key=True
    ),
    db.Column(
        'approved_assessment_id',
        db.Integer,
        db.ForeignKey('assessment.id', onupdate='CASCADE', ondelete='CASCADE'),
        primary_key=True
    ),
    db.Column(
        'approved_at',
        db.DateTime,
        default=lambda: datetime.now(),
        nullable=False
    )
)
Exemple #16
0
class Finding(Base, db.Model):
    __serialization__ = [
        AttributeConfiguration(name='id',
                               csv_sequence=1,
                               **supported_serialization),
        AttributeConfiguration(name='name', **supported_serialization),
        AttributeConfiguration(name='title', **supported_serialization),
        AttributeConfiguration(name='type', **supported_serialization),
        AttributeConfiguration(name='status', **supported_serialization),
        AttributeConfiguration(name='owasp_category',
                               **supported_serialization),
        AttributeConfiguration(name='owasp_mobile_category',
                               **supported_serialization),
        AttributeConfiguration(name='owisam_category',
                               **supported_serialization),
        AttributeConfiguration(name='description', **supported_serialization),
        AttributeConfiguration(name='solution', **supported_serialization),
        AttributeConfiguration(name='tech_risk', **supported_serialization),
        AttributeConfiguration(name='business_risk',
                               **supported_serialization),
        AttributeConfiguration(name='exploitability',
                               **supported_serialization),
        AttributeConfiguration(name='dissemination',
                               **supported_serialization),
        AttributeConfiguration(name='solution_complexity',
                               **supported_serialization),
        AttributeConfiguration(name='definition', **supported_serialization),
        AttributeConfiguration(name='references', **supported_serialization),
        AttributeConfiguration(name='affected_resources',
                               **supported_serialization),
        AttributeConfiguration(name='cvss_v3_vector',
                               **supported_serialization),
        AttributeConfiguration(name='cvss_v3_score',
                               **supported_serialization),
        AttributeConfiguration(name='cvss_v3_severity',
                               **supported_serialization),
        AttributeConfiguration(name='client_finding_id',
                               **supported_serialization),
        AttributeConfiguration(name='client_finding_code',
                               **supported_serialization),
        AttributeConfiguration(name='notes', **supported_serialization)
    ]

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), nullable=False)
    type = db.Column(Enum(FindingType), nullable=False)

    assessment_id = db.Column(
        db.Integer,
        db.ForeignKey('assessment.id', onupdate='CASCADE', ondelete='CASCADE'))
    assessment = db.relationship(Assessment,
                                 back_populates='findings',
                                 uselist=False)

    template_id = db.Column(
        db.Integer,
        db.ForeignKey('finding_template.id',
                      onupdate='CASCADE',
                      ondelete='SET NULL'))
    template = db.relationship(FindingTemplate, uselist=False)

    title = db.Column(db.String(128), nullable=False)
    status = db.Column(Enum(FindingStatus),
                       nullable=False,
                       default=FindingStatus.Pending)

    owasp_category = db.Column(Enum(OWASPCategory))
    owasp_mobile_category = db.Column(Enum(OWASPMobileTop10Category))
    owisam_category = db.Column(Enum(OWISAMCategory))

    description = db.Column(db.String())
    solution = db.Column(db.String())

    tech_risk = db.Column(Enum(Score), nullable=False)
    business_risk = db.Column(Enum(Score), nullable=False)
    exploitability = db.Column(Enum(Score), nullable=False)
    dissemination = db.Column(Enum(Score), nullable=False)
    solution_complexity = db.Column(Enum(Score), nullable=False)

    definition = db.Column(db.String(), nullable=False)
    references = db.Column(db.String(), nullable=False)

    affected_resources = db.relationship('AffectedResource',
                                         secondary=finding_affected_resource)

    cvss_v3_vector = db.Column(db.String(128))
    cvss_v3_score = db.Column(db.Float, default=0.0, nullable=False)

    client_finding_id = db.Column(db.Integer(), nullable=False)

    notes = db.Column(db.String())

    def update_affected_resources(self, resources: Collection[AnyStr]):
        resource_uris = []
        for resource in resources:
            resource = resource.strip()
            if not resource:
                continue  # Skip empty lines
            resource_uri = URIReference.from_string(resource)
            if resource_uri.is_valid(require_scheme=True):
                _resource_ok = resource_uri.scheme.lower() in {
                    'http', 'https'
                } and resource_uri.authority is not None
                _resource_ok = _resource_ok or (resource_uri.scheme == 'urn'
                                                and resource_uri.path
                                                is not None)
                if _resource_ok:
                    resource_uris.append(resource_uri)
                    continue

            raise ValueError('Invalid formatted URI: "{}"'.format(
                resource.strip()))

        affected_resources_to_add = set()

        for resource in resource_uris:
            if resource.authority is not None:
                # URL
                active_name = "{}://{}".format(resource.scheme,
                                               resource.authority)
                resource_route = resource.path

                if not resource_route:
                    resource_route = "/"

                if resource.query:
                    resource_route += "?" + resource.query

                if resource.fragment:
                    resource_route += "#" + resource.fragment
            elif resource.scheme == 'urn':
                # URN
                resource_name, *path = resource.path.split('/', 1)
                active_name = "{}:{}".format(resource.scheme, resource_name)
                resource_route = "/{}".format(path[0]) if path else None
            else:
                # TODO: this should never happen. Make some warning.
                continue

            active = Active.query.filter_by(assessment=self.assessment,
                                            name=active_name).first()

            if not active:
                active = Active(assessment=self.assessment, name=active_name)
                affected_resource = AffectedResource(active=active,
                                                     route=resource_route)
                active.active_resources.append(affected_resource)
                db.session.add(active)
                db.session.add(affected_resource)
            else:
                affected_resource = AffectedResource.query.filter_by(
                    active=active, route=resource_route).first()

                if not affected_resource:
                    affected_resource = AffectedResource(active=active,
                                                         route=resource_route)
                    active.active_resources.append(affected_resource)
                    db.session.add(affected_resource)

            affected_resources_to_add.add(affected_resource)
            db.session.commit()

        for affected_resource in self.affected_resources:
            if affected_resource not in affected_resources_to_add:
                affected_resource.delete_last_reference()

        for affected_resource in affected_resources_to_add:
            self.affected_resources.append(affected_resource)

        db.session.commit()

    @property
    def cvss_v3_severity(self):
        score = self.cvss_v3_score
        if score == 0:
            return Score.Info
        elif 0 < score < 4:
            return Score.Low
        elif 4 <= score < 7:
            return Score.Medium
        elif 7 <= score < 9:
            return Score.High
        else:
            return Score.Critical

    @property
    def client_finding_code(self):
        return self.assessment.client.format_finding_code(self)

    @classmethod
    def build_from_template(cls, template: FindingTemplate,
                            assessment: Assessment):
        lang = assessment.lang
        client = assessment.client
        translation: FindingTemplateTranslation = None
        for t in template.translations:
            translation = t
            if t.lang == lang:
                break

        return Finding(name=template.name,
                       type=template.type,
                       tech_risk=template.tech_risk,
                       business_risk=template.business_risk,
                       exploitability=template.exploitability,
                       dissemination=template.dissemination,
                       solution_complexity=template.solution_complexity,
                       owasp_category=template.owasp_category,
                       owasp_mobile_category=template.owasp_mobile_category,
                       owisam_category=template.owisam_category,
                       template=template,
                       title=translation.title,
                       definition=translation.definition,
                       references=translation.references,
                       description=translation.description,
                       assessment=assessment,
                       cvss_v3_vector=template.cvss_v3_vector,
                       cvss_v3_score=template.cvss_v3_score,
                       client_finding_id=client.generate_finding_counter())
Exemple #17
0
import os

from sarna.core.config import config
from sarna.model.base import Base, db

__all__ = ['Client', 'Template', 'client_management', 'client_audit']

client_management = db.Table(
    'client_management',
    db.Column(
        'managed_client_id',
        db.Integer,
        db.ForeignKey('client.id', onupdate="CASCADE", ondelete="CASCADE"), primary_key=True
    ),
    db.Column(
        'manager_id',
        db.Integer,
        db.ForeignKey('user.id', onupdate="CASCADE", ondelete="CASCADE"), primary_key=True
    )
)
client_audit = db.Table(
    'client_audit',
    db.Column(
        'audited_client_id',
        db.Integer,
        db.ForeignKey('client.id', onupdate="CASCADE", ondelete="CASCADE"), primary_key=True
    ),
    db.Column(
        'auditor_id',
        db.Integer,
        db.ForeignKey('user.id', onupdate="CASCADE", ondelete="CASCADE"), primary_key=True
Exemple #18
0
from datetime import datetime

from sqlathanor import AttributeConfiguration

from sarna.core.config import config
from sarna.model.base import Base, db, supported_serialization

__all__ = ['Client', 'Template', 'client_management', 'client_audit']

client_management = db.Table(
    'client_management',
    db.Column('managed_client_id',
              db.Integer,
              db.ForeignKey('client.id',
                            onupdate="CASCADE",
                            ondelete="CASCADE"),
              primary_key=True),
    db.Column('manager_id',
              db.Integer,
              db.ForeignKey('user.id', onupdate="CASCADE", ondelete="CASCADE"),
              primary_key=True))
client_audit = db.Table(
    'client_audit',
    db.Column('audited_client_id',
              db.Integer,
              db.ForeignKey('client.id',
                            onupdate="CASCADE",
                            ondelete="CASCADE"),
              primary_key=True),
    db.Column('auditor_id',
              db.Integer,
Exemple #19
0
class Template(Base, db.Model):
    name = db.Column(db.String(32), primary_key=True)
    client_id = db.Column(db.Integer, db.ForeignKey('client.id', onupdate="CASCADE", ondelete="CASCADE"), primary_key=True)

    description = db.Column(db.String(128))
    file = db.Column(db.String(128), nullable=False)