Beispiel #1
0
class Notification(db.Model):
    """Our fancy notification class."""
    id = db.Column(db.Integer(), primary_key=True)

    category = db.Column(db.String())
    object_uri = db.Column(db.String())
    icon = db.Column(db.String())

    seen = db.Column(db.Boolean())
    acknowledged = db.Column(db.Boolean())

    created_by_actor_id = db.Column(
        db.Integer(),
        db.ForeignKey('actors.id',
                      ondelete='SET NULL',
                      name='fk_notification_by_actor'),
        nullable=True,
    )
    for_identity_id = db.Column(
        db.Integer(),
        db.ForeignKey('identities.id',
                      ondelete='CASCADE',
                      name='fk_notification_for_identity'),
    )
    created = db.Column(db.DateTime())
Beispiel #2
0
class Follow(db.Model):
    """Subscriptions are a link between actors. When you subscribe, you are
    saying, "yes, please, show me the things that you create."

    This activity is explicitly supported by ActivityPub, but I call it out
    explicitly for convenience. Explicitly..
    """
    __tablename__ = 'follows'

    id = db.Column(db.Integer(), primary_key=True)
    actor_id = db.Column(
        db.Integer(),
        db.ForeignKey('actors.id', ondelete='CASCADE', name='fk_follow_actor'),
    )
    target_actor_id = db.Column(
        db.Integer(),
        db.ForeignKey('actors.id',
                      ondelete='CASCADE',
                      name='fk_follow_target_actor'),
    )

    # Waiting for account review
    pending_review = db.Column(db.Boolean())
    # Approved by actor owner
    approved = db.Column(db.Boolean())
    # Hard rejected by owner, all future requests will also be blocked
    blocked = db.Column(db.Boolean())

    created = db.Column(db.DateTime())
    last_updated = db.Column(db.DateTime())
Beispiel #3
0
class Account(db.Model):
    """Login details for user actors are stored here. Accounts in lamia are
    best explained as being containers for blogs and identities, where both of
    these are 1-to-1 connections to ActivityPub actors.

    Basically, an account on lamia can own multiple identities and blogs. These
    identities and blogs are what others see when someone with an account makes
    a post.

    TODO: to prevent intentional/unintentional abuse, this needs to be VERY
    transparent.
    """
    __tablename__ = 'accounts'

    id = db.Column(db.Integer(), primary_key=True)
    primary_identity_id = db.Column(
        db.Integer(),
        db.ForeignKey('identities.id',
                      ondelete='SET NULL',
                      name='fk_actor_primary_identity'),
        nullable=True,
    )
    email_address = db.Column(db.String())
    # Should be a hash encrypted by scrypt
    password = db.Column(db.String())
    created = db.Column(db.DateTime())
    # Activates low bandwidth mode to control image transmissions
    low_bandwidth = db.Column(db.Boolean())
    # Content should be hidden and attachments marked as sensitive by default
    sensitive_content = db.Column(db.Boolean())
    # All follows require confirmation if this is true
    approval_for_follows = db.Column(db.Boolean())

    # Either someone is banned or not banned.
    # Problematic users, nazis, etc should be banned without mercy or guilt.
    # Just f*****g do it, they aren't worth having around.
    # You shouldn't even have to think about it. Do it. Do it now.
    # Unless a deletion is desired. In which case, there is a way to do this.
    banned = db.Column(db.Boolean())
    # Profile customizations enabled/disabled for this account
    disable_profile_customizations = db.Column(db.Boolean())

    def set_password(self, password: str) -> None:
        """Hash a plaintext password and set the class property."""
        self.password = bcrypt.hashpw(password.encode(),
                                      bcrypt.gensalt()).decode()

    def check_password(self, password: str) -> bool:
        """Compare a plaintext password to the class property."""
        return bcrypt.checkpw(password.encode(), self.password.encode())
Beispiel #4
0
class Filter(db.Model):
    """Filters are best described as a more nuanced version of muting, allowing
    specific words to be removed from a timeline or minimized within the
    timeline.
    """
    __tablename__ = 'filters'

    id = db.Column(db.Integer(), primary_key=True)
    account_id = db.Column(
        db.Integer(),
        db.ForeignKey('accounts.id',
                      ondelete='CASCADE',
                      name='fk_filter_account'),
    )

    query = db.Column(db.String)
    hide = db.Column(db.Boolean)
    minimize = db.Column(db.Boolean)
    filter_actor_id = db.Column(
        db.Integer(),
        db.ForeignKey('actors.id',
                      ondelete='CASCADE',
                      name='fk_filter_applied_to_actor'),
    )

    created = db.Column(db.DateTime())
    duration = db.Column(db.Interval())
    forever = db.Column(db.Boolean())
Beispiel #5
0
class Import(db.Model):
    """Followed, muted, and blocked actors can be imported from ActivityPub
    files.

    These imports should not run automatically because of the potential
    for abuse, particularly from "follow" actions. Instead, they are stored here
    and can be selectively authorized to move forward by moderators.
    """
    __tablename__ = 'imports'

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

    request_by_account_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'accounts.id', ondelete='CASCADE', name='fk_import_requested_by'),
    )
    request_for_identity_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'identities.id', ondelete='CASCADE',
            name='fk_import_for_identity'),
    )
    data_to_import = db.Column(JSONB())

    created = db.Column(db.DateTime())
    last_updated = db.Column(db.DateTime())
    processed = db.Column(db.DateTime())

    allowed = db.Column(db.Boolean())
Beispiel #6
0
class Actor(db.Model):
    """Actually, actors are objects (or ActivityStreams), but I call them out
    explicitly, because they are a key component in how lamia sees the
    fediverse.

    From the ActivityPub specifications:
    https://www.w3.org/TR/activitypub/#actors

    All the world’s a stage,
    And all the identities and blogs merely actors
    - Snakespeare, As You Lamia It
    """
    __tablename__ = 'actors'

    id = db.Column(db.Integer(), primary_key=True)
    actor_type = db.Column(db.String())
    private_key = db.Column(db.String(), nullable=True)

    display_name = db.Column(db.String())
    user_name = db.Column(db.String())
    uri = db.Column(db.String())
    local = db.Column(db.Boolean())

    created = db.Column(db.DateTime())
    last_updated = db.Column(db.DateTime())

    data = db.Column(JSONB())

    def generate_keys(self):
        """Create new keys and stuff them into an already created actor"""
        key = RSA.generate(2048)
        self.private_key = key.export_key("PEM").decode()

        try:
            del self.data['publicKey']
        except KeyError:
            pass

        self.data['publicKey'] = {
            'id': f'{BASE_URL}/u/{self.user_name}#main-key',
            'owner': f'{BASE_URL}/u/{self.user_name}',
            'publicKeyPem': key.publickey().export_key("PEM").decode()
        }

    # Convenience fields for local actors.
    identity_id = db.Column(db.Integer(),
                            db.ForeignKey('identities.id',
                                          ondelete='SET NULL',
                                          name='fk_actor_identity'),
                            nullable=True)
    blog_id = db.Column(db.Integer(),
                        db.ForeignKey('blogs.id',
                                      ondelete='SET NULL',
                                      name='fk_actor_blog'),
                        nullable=True)
Beispiel #7
0
class Blog(db.Model):
    """An account can have more than one blog. Each blog connects an
    account to a single ActivityPub actor.
    """
    __tablename__ = 'blogs'

    id = db.Column(db.Integer(), primary_key=True)
    actor_id = db.Column(
        db.Integer(),
        db.ForeignKey('actors.id', ondelete='CASCADE', name='fk_blog_actor'),
    )

    # Non-standard profile customizations
    page_background_color = db.Column(db.String())
    page_background_image = db.Column(db.String())
    page_background_tiled = db.Column(db.Boolean())
    page_background_static = db.Column(db.Boolean())
    section_background_color = db.Column(db.String())
    section_header_image = db.Column(db.String())
    section_text_color = db.Column(db.String())
    section_text_shadow = db.Column(db.Boolean())
    section_text_shadow_color = db.Column(db.Boolean())
    # Can be disabled
    disabled = db.Column(db.Boolean())
    # Can be soft deleted. To permanently delete, delete the associated account.
    # This is done to prevent abuse in the form of rapidly created temporary
    # accounts being used to torment and then removed after a block/mute.
    deleted = db.Column(db.Boolean())
    created = db.Column(db.DateTime())
    last_updated = db.Column(db.DateTime())
Beispiel #8
0
class Report(db.Model):
    """Reports are a necessary part of any online social environment. They are
    a way to flag local content for moderators on a local site, and they
    can be created and sent to this site from other sites.
    """
    __tablename__ = 'reports'

    id = db.Column(db.Integer(), primary_key=True)
    original_content = db.Column(db.String())
    content_uri = db.Column(db.String())
    target_actor_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'actors.id', ondelete='SET NULL', name='fk_report_target_actor'),
        nullable=True,
    )

    report_by_actor_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'actors.id',
            ondelete='SET NULL',
            name='fk_report_created_by_actor'),
        nullable=True,
    )
    current_status = db.Column(db.String())
    assigned_to_account_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'accounts.id',
            ondelete='SET NULL',
            name='fk_report_assigned_to_account'),
        nullable=True,
    )
    comment_count = db.Column(db.Integer())

    created = db.Column(db.DateTime())
    last_updated = db.Column(db.DateTime())
    resolved = db.Column(db.Boolean())
    marked_resolved_by_account_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'accounts.id',
            ondelete='SET NULL',
            name='fk_report_marked_resolved_by_actor'),
        nullable=True)
Beispiel #9
0
class Attachments(db.Model):
    """An attachment is an image tied to some kind of ActivityPub object.

    TODO: We can look into non-image attachments and probably dismiss the
    possibility of them later on.
    """
    __tablename__ = 'attachments'

    id = db.Column(db.Integer(), primary_key=True)
    uploaded_by_actor_uri = db.Column(db.String())
    alt_text = db.Column(db.String())

    storage_path = db.Column(db.String())
    storage_uri = db.Column(db.String())
    remote_uri = db.Column(db.String())

    size_in_bytes = db.Column(db.Integer())
    local = db.Column(db.Boolean())

    created = db.Column(db.DateTime())
Beispiel #10
0
class DomainMute(db.Model):
    """A domain mute is a moderate server to server moderation action.

    A domain mute is a created at the server level, and either hides the domains'
    activities from the federated timeline or minimizes them.
    """
    __tablename__ = 'domain_mutes'

    id = db.Column(db.Integer(), primary_key=True)
    domain = db.Column(db.String())
    created = db.Column(db.DateTime())
    duration = db.Column(db.Interval())
    forever = db.Column(db.Boolean())
    created_by_account_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'accounts.id',
            ondelete='SET NULL',
            name='fk_domainmute_created_by_account'),
        nullable=True)
Beispiel #11
0
class ReportComment(db.Model):
    """Allow moderator to moderator talk in a report, should work like chat
    messages."""
    __tablename__ = 'report_comments'

    message = db.Column(db.String())
    created_by_account_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'accounts.id',
            ondelete='SET NULL',
            name='fk_reportcomment_created_by_account'),
        nullable=True)
    created = db.Column(db.DateTime())

    report_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'reports.id', ondelete='CASCADE', name='fk_reportcomment_report'))
    # Changing the status of a report should create a comment where the message
    # is something like 'changed status from ignored to open'.
    status_change = db.Column(db.Boolean())
Beispiel #12
0
class ActorMute(db.Model):
    """An actor block is a moderate server to client moderation action.

    An actor mute is a created at the server level, and either hides the actor's
    activities from the federated timeline or minimizes them.
    """
    __tablename__ = 'actor_mutes'

    id = db.Column(db.Integer(), primary_key=True)
    target_actor_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'actors.id', ondelete='CASCADE', name='fk_actormute_target_actor'),
    )
    created = db.Column(db.DateTime())
    duration = db.Column(db.Interval())
    forever = db.Column(db.Boolean())
    created_by_account_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'accounts.id',
            ondelete='SET NULL',
            name='fk_actormute_created_by_account'),
        nullable=True)