Add LocalUser and RemoteUser and migration
This commit is contained in:
parent
b4b04522bf
commit
aa9ba3ed80
@ -1429,3 +1429,123 @@ def remove_activityintermediator(db):
|
||||
|
||||
# Commit the changes
|
||||
db.commit()
|
||||
|
||||
##
|
||||
# Migrations for converting the User model into a Local and Remote User
|
||||
# setup.
|
||||
##
|
||||
|
||||
class LocalUser_V0(declarative_base()):
|
||||
__tablename__ = "core__local_users"
|
||||
|
||||
id = Column(Integer, ForeignKey(User.id), primary_key=True)
|
||||
username = Column(Unicode, nullable=False, unique=True)
|
||||
email = Column(Unicode, nullable=False)
|
||||
pw_hash = Column(Unicode)
|
||||
|
||||
wants_comment_notification = Column(Boolean, default=True)
|
||||
wants_notifications = Column(Boolean, default=True)
|
||||
license_preference = Column(Unicode)
|
||||
uploaded = Column(Integer, default=0)
|
||||
upload_limit = Column(Integer)
|
||||
|
||||
class RemoteUser_V0(declarative_base()):
|
||||
__tablename__ = "core__remote_users"
|
||||
|
||||
id = Column(Integer, ForeignKey(User.id), primary_key=True)
|
||||
webfinger = Column(Unicode, unique=True)
|
||||
|
||||
@RegisterMigration(32, MIGRATIONS)
|
||||
def federation_user_create_tables(db):
|
||||
"""
|
||||
Create all the tables
|
||||
"""
|
||||
# Create tables needed
|
||||
LocalUser_V0.__table__.create(db.bind)
|
||||
RemoteUser_V0.__table__.create(db.bind)
|
||||
db.commit()
|
||||
|
||||
# Create the fields
|
||||
metadata = MetaData(bind=db.bind)
|
||||
user_table = inspect_table(metadata, "core__users")
|
||||
|
||||
updated_column = Column(
|
||||
"updated",
|
||||
DateTime,
|
||||
default=datetime.datetime.utcnow
|
||||
)
|
||||
updated_column.create(user_table)
|
||||
|
||||
name_column = Column(
|
||||
"name",
|
||||
Unicode
|
||||
)
|
||||
name_column.create(user_table)
|
||||
|
||||
db.commit()
|
||||
|
||||
@RegisterMigration(33, MIGRATIONS)
|
||||
def federation_user_migrate_data(db):
|
||||
"""
|
||||
Migrate the data over to the new user models
|
||||
"""
|
||||
metadata = MetaData(bind=db.bind)
|
||||
|
||||
user_table = inspect_table(metadata, "core__users")
|
||||
local_user_table = inspect_table(metadata, "core__local_users")
|
||||
|
||||
for user in db.execute(user_table.select()):
|
||||
db.execute(local_user_table.insert().values(
|
||||
id=user.id,
|
||||
username=user.username,
|
||||
email=user.email,
|
||||
pw_hash=user.pw_hash,
|
||||
wants_comment_notification=user.wants_comment_notification,
|
||||
wants_notifications=user.wants_notifications,
|
||||
license_preference=user.license_preference,
|
||||
uploaded=user.uploaded,
|
||||
upload_limit=user.upload_limit
|
||||
))
|
||||
|
||||
db.execute(user_table.update().where(user_table.c.id==user.id).values(
|
||||
updated=user.created
|
||||
))
|
||||
|
||||
db.commit()
|
||||
|
||||
@RegisterMigration(34, MIGRATIONS)
|
||||
def federation_remove_fields(db):
|
||||
"""
|
||||
This removes the fields from User model which aren't shared
|
||||
"""
|
||||
metadata = MetaData(bind=db.bind)
|
||||
|
||||
user_table = inspect_table(metadata, "core__users")
|
||||
|
||||
# Remove the columns moved to LocalUser from User
|
||||
username_column = user_table.columns["username"]
|
||||
username_column.drop()
|
||||
|
||||
email_column = user_table.columns["email"]
|
||||
email_column.drop()
|
||||
|
||||
pw_hash_column = user_table.columns["pw_hash"]
|
||||
pw_hash_column.drop()
|
||||
|
||||
wcn_column = user_table.columns["wants_comment_notification"]
|
||||
wcn_column.drop()
|
||||
|
||||
wants_notifications_column = user_table.columns["wants_notifications"]
|
||||
wants_notifications_column.drop()
|
||||
|
||||
license_preference_column = user_table.columns["license_preference"]
|
||||
license_preference_column.drop()
|
||||
|
||||
uploaded_column = user_table.columns["uploaded"]
|
||||
uploaded_column.drop()
|
||||
|
||||
upload_limit_column = user_table.columns["upload_limit"]
|
||||
upload_limit_column.drop()
|
||||
|
||||
db.commit()
|
||||
|
||||
|
@ -222,62 +222,29 @@ class Location(Base):
|
||||
|
||||
class User(Base, UserMixin):
|
||||
"""
|
||||
TODO: We should consider moving some rarely used fields
|
||||
into some sort of "shadow" table.
|
||||
Base user that is common amongst LocalUser and RemoteUser.
|
||||
|
||||
This holds all the fields which are common between both the Local and Remote
|
||||
user models.
|
||||
|
||||
NB: ForeignKeys should reference this User model and NOT the LocalUser or
|
||||
RemoteUser models.
|
||||
"""
|
||||
__tablename__ = "core__users"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
username = Column(Unicode, nullable=False, unique=True)
|
||||
# Note: no db uniqueness constraint on email because it's not
|
||||
# reliable (many email systems case insensitive despite against
|
||||
# the RFC) and because it would be a mess to implement at this
|
||||
# point.
|
||||
email = Column(Unicode, nullable=False)
|
||||
pw_hash = Column(Unicode)
|
||||
created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
|
||||
# Intented to be nullable=False, but migrations would not work for it
|
||||
# set to nullable=True implicitly.
|
||||
wants_comment_notification = Column(Boolean, default=True)
|
||||
wants_notifications = Column(Boolean, default=True)
|
||||
license_preference = Column(Unicode)
|
||||
url = Column(Unicode)
|
||||
bio = Column(UnicodeText) # ??
|
||||
uploaded = Column(Integer, default=0)
|
||||
upload_limit = Column(Integer)
|
||||
bio = Column(UnicodeText)
|
||||
name = Column(Unicode)
|
||||
|
||||
created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
|
||||
updated = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
|
||||
|
||||
location = Column(Integer, ForeignKey("core__locations.id"))
|
||||
|
||||
# Lazy getters
|
||||
get_location = relationship("Location", lazy="joined")
|
||||
|
||||
## TODO
|
||||
# plugin data would be in a separate model
|
||||
|
||||
def __repr__(self):
|
||||
return '<{0} #{1} {2} {3} "{4}">'.format(
|
||||
self.__class__.__name__,
|
||||
self.id,
|
||||
'verified' if self.has_privilege(u'active') else 'non-verified',
|
||||
'admin' if self.has_privilege(u'admin') else 'user',
|
||||
self.username)
|
||||
|
||||
def delete(self, **kwargs):
|
||||
"""Deletes a User and all related entries/comments/files/..."""
|
||||
# Collections get deleted by relationships.
|
||||
|
||||
media_entries = MediaEntry.query.filter(MediaEntry.uploader == self.id)
|
||||
for media in media_entries:
|
||||
# TODO: Make sure that "MediaEntry.delete()" also deletes
|
||||
# all related files/Comments
|
||||
media.delete(del_orphan_tags=False, commit=False)
|
||||
|
||||
# Delete now unused tags
|
||||
# TODO: import here due to cyclic imports!!! This cries for refactoring
|
||||
from mediagoblin.db.util import clean_orphan_tags
|
||||
clean_orphan_tags(commit=False)
|
||||
|
||||
# Delete user, pass through commit=False/True in kwargs
|
||||
super(User, self).delete(**kwargs)
|
||||
_log.info('Deleted user "{0}" account'.format(self.username))
|
||||
|
||||
def has_privilege(self, privilege, allow_admin=True):
|
||||
"""
|
||||
This method checks to make sure a user has all the correct privileges
|
||||
@ -309,19 +276,89 @@ class User(Base, UserMixin):
|
||||
"""
|
||||
return UserBan.query.get(self.id) is not None
|
||||
|
||||
|
||||
def serialize(self, request):
|
||||
published = UTC.localize(self.created)
|
||||
updated = UTC.localize(self.updated)
|
||||
user = {
|
||||
"id": "acct:{0}@{1}".format(self.username, request.host),
|
||||
"published": published.isoformat(),
|
||||
"preferredUsername": self.username,
|
||||
"displayName": "{0}@{1}".format(self.username, request.host),
|
||||
"updated": updated.isoformat(),
|
||||
"objectType": self.object_type,
|
||||
"pump_io": {
|
||||
"shared": False,
|
||||
"followed": False,
|
||||
},
|
||||
}
|
||||
|
||||
if self.bio:
|
||||
user.update({"summary": self.bio})
|
||||
if self.url:
|
||||
user.update({"url": self.url})
|
||||
if self.location:
|
||||
user.update({"location": self.get_location.serialize(request)})
|
||||
|
||||
def unserialize(self, data):
|
||||
if "summary" in data:
|
||||
self.bio = data["summary"]
|
||||
|
||||
if "location" in data:
|
||||
Location.create(data, self)
|
||||
|
||||
class LocalUser(User):
|
||||
""" This represents a user registered on this instance """
|
||||
__tablename__ = "core__local_users"
|
||||
|
||||
id = Column(Integer, ForeignKey("core__users.id"), primary_key=True)
|
||||
username = Column(Unicode, nullable=False, unique=True)
|
||||
# Note: no db uniqueness constraint on email because it's not
|
||||
# reliable (many email systems case insensitive despite against
|
||||
# the RFC) and because it would be a mess to implement at this
|
||||
# point.
|
||||
email = Column(Unicode, nullable=False)
|
||||
pw_hash = Column(Unicode)
|
||||
|
||||
# Intented to be nullable=False, but migrations would not work for it
|
||||
# set to nullable=True implicitly.
|
||||
wants_comment_notification = Column(Boolean, default=True)
|
||||
wants_notifications = Column(Boolean, default=True)
|
||||
license_preference = Column(Unicode)
|
||||
uploaded = Column(Integer, default=0)
|
||||
upload_limit = Column(Integer)
|
||||
|
||||
## TODO
|
||||
# plugin data would be in a separate model
|
||||
|
||||
def __repr__(self):
|
||||
return '<{0} #{1} {2} {3} "{4}">'.format(
|
||||
self.__class__.__name__,
|
||||
self.id,
|
||||
'verified' if self.has_privilege(u'active') else 'non-verified',
|
||||
'admin' if self.has_privilege(u'admin') else 'user',
|
||||
self.username)
|
||||
|
||||
def delete(self, **kwargs):
|
||||
"""Deletes a User and all related entries/comments/files/..."""
|
||||
# Collections get deleted by relationships.
|
||||
|
||||
media_entries = MediaEntry.query.filter(MediaEntry.uploader == self.id)
|
||||
for media in media_entries:
|
||||
# TODO: Make sure that "MediaEntry.delete()" also deletes
|
||||
# all related files/Comments
|
||||
media.delete(del_orphan_tags=False, commit=False)
|
||||
|
||||
# Delete now unused tags
|
||||
# TODO: import here due to cyclic imports!!! This cries for refactoring
|
||||
from mediagoblin.db.util import clean_orphan_tags
|
||||
clean_orphan_tags(commit=False)
|
||||
|
||||
# Delete user, pass through commit=False/True in kwargs
|
||||
super(User, self).delete(**kwargs)
|
||||
_log.info('Deleted user "{0}" account'.format(self.username))
|
||||
|
||||
def serialize(self, request):
|
||||
user = {
|
||||
"id": "acct:{0}@{1}".format(self.username, request.host),
|
||||
"preferredUsername": self.username,
|
||||
"displayName": "{0}@{1}".format(self.username, request.host),
|
||||
"links": {
|
||||
"self": {
|
||||
"href": request.urlgen(
|
||||
@ -347,21 +384,23 @@ class User(Base, UserMixin):
|
||||
},
|
||||
}
|
||||
|
||||
if self.bio:
|
||||
user.update({"summary": self.bio})
|
||||
if self.url:
|
||||
user.update({"url": self.url})
|
||||
if self.location:
|
||||
user.update({"location": self.get_location.serialize(request)})
|
||||
|
||||
user.update(super(LocalUser, self).serialize(request))
|
||||
return user
|
||||
|
||||
def unserialize(self, data):
|
||||
if "summary" in data:
|
||||
self.bio = data["summary"]
|
||||
class RemoteUser(User):
|
||||
""" User that is on another (remote) instance """
|
||||
__tablename__ = "core__remote_users"
|
||||
|
||||
id = Column(Integer, ForeignKey("core__users.id"), primary_key=True)
|
||||
webfinger = Column(Unicode, unique=True)
|
||||
|
||||
def __repr__(self):
|
||||
return "<{0} #{1} {2}>".format(
|
||||
self.__class__.__name__,
|
||||
self.id,
|
||||
self.webfinger
|
||||
)
|
||||
|
||||
if "location" in data:
|
||||
Location.create(data, self)
|
||||
|
||||
class Client(Base):
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user