示例#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())
示例#2
0
class Object(db.Model):
    """Objects are the Things in the fediverse.

    From the ActivityPub specifications:
    https://www.w3.org/TR/activitypub/#obj
    """
    __tablename__ = 'objects'

    id = db.Column(db.Integer(), primary_key=True)
    uri = db.Column(db.String())
    actor_uri = db.Column(db.String())
    reply_to_uri = db.Column(db.String())
    object_type = db.Column(db.String())

    created = db.Column(db.DateTime())
    created_by_actor_id = db.Column(
        db.Integer(),
        db.ForeignKey('actors.id',
                      ondelete='SET NULL',
                      name='fk_object_created_by_actor'),
        nullable=True,
    )
    last_updated = db.Column(db.DateTime())

    data = db.Column(JSONB())
示例#3
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())
示例#4
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)
示例#5
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())
示例#6
0
class Emoji(db.Model):
    """A mapping of images, replacement text, and description text all of which
    exists for the purpose of proliferating blob emojis and upside down smiley
    faces throughout the fediverse.
    """
    __tablename__ = 'emojis'

    id = db.Column(db.Integer(), primary_key=True)
    # A path to the image in /statics
    image = db.Column(db.String())
    replacement = db.Column(db.String())
    # Only needed if replacement doesn't do it justice
    description = db.Column(db.String())
    # for linear algebra with emojis, in this essay, i will...
    set_name = db.Column(db.String())
示例#7
0
class Tag(db.Model):
    """The metaphysical concept of the hashtag, made real and without conceit."""
    __tablename__ = 'tags'

    id = db.Column(db.Integer(), primary_key=True)
    tag = db.Column(db.String())
    created = db.Column(db.DateTime())
示例#8
0
class Bookmark(db.Model):
    """A bookmark is a "saved" link to an ActivityPub object that is not
    an actor."""
    __tablename__ = 'boookmarks'

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

    object_id = db.Column(
        db.Integer(),
        db.ForeignKey('objects.id',
                      ondelete='CASCADE',
                      name='fk_bookmark_object'))

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

    group_id = db.Column(db.Integer(),
                         db.ForeignKey('bookmark_groups.id',
                                       ondelete='SET NULL',
                                       name='fk_bookmark_group'),
                         nullable=True)

    created = db.Column(db.DateTime())
示例#9
0
class BookmarkGroup(db.Model):
    """Bookmark groups can be implicitly created to organize bookmarks"""
    __tablename__ = 'bookmark_groups'

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

    created = db.Column(db.DateTime())
示例#10
0
class Activity(db.Model):
    """Activities are things that happen to other things on the fediverse.

    From the ActivityPub specifications:
    * https://www.w3.org/TR/activitypub/#client-to-server-interactions
    * https://www.w3.org/TR/activitypub/#server-to-server-interactions
    """
    __tablename__ = 'activities'

    id = db.Column(db.Integer(), primary_key=True)
    uri = db.Column(db.String())
    object_uri = db.Column(db.String())
    actor_uri = db.Column(db.String())
    activity_type = db.Column(db.String())

    created = db.Column(db.DateTime())
    data = db.Column(JSONB())
示例#11
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)
示例#12
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())
示例#13
0
class OauthToken(db.Model):
    """A token created for our lovely API."""
    __tablename__ = 'oauth_tokens'

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

    access_token = db.Column(db.String())
    token_secret = db.Column(db.String())
    expires = db.Column(db.DateTime())
    created = db.Column(db.DateTime())

    def set_access_token(self, payload: dict, days: int = 7) -> str:
        """Encode a payload for this token."""
        now = pendulum.now()
        duration = pendulum.duration(days=days)
        expires = now + duration

        payload['created'] = now.to_iso8601_string()
        payload['expiration'] = expires.to_iso8601_string()

        self.expires = expires.naive()
        self.created = now.naive()

        secret = os.urandom(24).hex()
        self.token_secret = secret
        self.access_token = jwt.encode(
            payload, secret, algorithm='HS256').decode()
        return self.access_token

    def decode_access_token(self, token: str) -> dict:
        """Decode a previously encoded token."""
        return jwt.decode(token, self.token_secret, algorithms=['HS256'])
示例#14
0
class Setting(db.Model):
    """A basic key and value storage for settings.

    Note: From a philosophical standpoint, settings should all be optional, and
    built around non-essential functionality. The installation of lamia should
    require as little gymnastics as possible and should be, dare I say, Fun.

    TODO: figure out settings we may need here.
    """
    __tablename__ = 'settings'

    id = db.Column(db.Integer(), primary_key=True)
    key = db.Column(db.String())
    value = db.Column(JSONB())
示例#15
0
class DomainEmailBlock(db.Model):
    """Email domain blocks prevent harmful registrations and registrations
    from spammers.
    """
    __tablename__ = 'domain_email_block'

    id = db.Column(db.Integer(), primary_key=True)
    domain = db.Column(db.String())
    created = db.Column(db.DateTime())
    created_by_account_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'accounts.id',
            ondelete='SET NULL',
            name='fk_domainemailblock_created_by_account'),
        nullable=True)
示例#16
0
class ModerationLog(db.Model):
    """Quis custodiet ipsos custodes?

    Instance admins and other moderators should keep an eye on log entries
    and log entries should result in notifications, in theory.
    """
    __tablename__ = 'moderation_logs'
    description = db.Column(db.String())

    created = db.Column(db.DateTime())
    created_by_account_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'accounts.id',
            ondelete='SET NULL',
            name='fk_moderationlog_account'))
示例#17
0
class Feed(db.Model):
    """A feed is a set of actors that you want to create a custom timeline
    for. As an example, you could create a feed labeled "friends" for calling
    out your friends' blog posts and statuses.

    It's worth noting that you need to subscribe to an actor before this works.
    """
    __tablename__ = 'feeds'

    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String())
    identity_id = db.Column(
        db.Integer(),
        db.ForeignKey('identities.id',
                      ondelete='CASCADE',
                      name='fk_feed_identity'),
    )
    created = db.Column(db.DateTime())
示例#18
0
class DomainCensor(db.Model):
    """A domain censor is a light server to server moderation action.

    An domain censor is a created at the server level, and forces all activities
    from a specific domain to either appear content warned or minimized.
    """
    __tablename__ = 'domain_censors'

    id = db.Column(db.Integer(), primary_key=True)
    domain = db.Column(db.String())
    created = db.Column(db.DateTime())
    created_by_account_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'accounts.id',
            ondelete='SET NULL',
            name='fk_domaincensor_created_by_account'),
        nullable=True)
示例#19
0
class OauthApplication(db.Model):
    """An external application or service."""
    __tablename__ = 'oauth_applications'

    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String())
    website = db.Column(db.String())

    redirect_uri = db.Column(db.String())
    scopes = db.Column(db.String())
    client_id = db.Column(db.String())
    client_secret = db.Column(db.String())

    owner_id = db.Column(db.Integer())

    created = db.Column(db.DateTime())
    last_updated = db.Column(db.String())
示例#20
0
class DomainBlock(db.Model):
    """A domain block is a severe server to client moderation action.

    A domain block is a created at the server level, and probits all actors on
    a blocked site from interacting with the local site. It also breaks
    follows between actors on this site and the site that is blocked.
    """
    __tablename__ = 'domain_blocks'

    id = db.Column(db.Integer(), primary_key=True)
    domain = db.Column(db.String())
    created = db.Column(db.DateTime())
    created_by_account_id = db.Column(
        db.Integer(),
        db.ForeignKey(
            'accounts.id',
            ondelete='SET NULL',
            name='fk_domainblock_created_by_account'),
        nullable=True)
示例#21
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)
示例#22
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())