This blog post will showcase introduction of new owner role for users in Open Event Frontend. Now a user associated with organizing of an event can have any of the following roles:
- Owner
- Organizer
- Co-organizer
- Track-organizer
- Moderator
- Registrar
Till now, the user creating the event had organizer role which was not exclusive. An organizer can invite other users to be organizer. So, later we couldn’t give exclusive rights to the event creator due to this.
But there can only be a single owner of an event. So, the introduction of new owner role will help us distinguish the owner and give him/her exclusive rights for the event.
This refactor involved a lot of changes. Let’s go step by step:
- I updated the role of the user creating the event to be owner by default. For this, we query the user and owner role and use it to create a new UserEventRoles object and save it to database. Then we create a role invite object using the user email, role title, event id and role id.
def after_create_object(self, event, data, view_kwargs): ... user = User.query.filter_by(id=view_kwargs['user_id']).first() role = Role.query.filter_by(name=OWNER).first() uer = UsersEventsRoles(user, event, role) save_to_db(uer, 'Event Saved') role_invite = RoleInvite(user.email, role.title_name, event.id, role.id, datetime.now(pytz.utc), status='accepted') save_to_db(role_invite, 'Owner Role Invite Added')
- We included a new function is_owner to permission_manager helper which checks if the current_user is owner of the event passed in kwargs. If the user is not the owner, ForbiddenError is returned.
@jwt_required def is_owner(view, view_args, view_kwargs, *args, **kwargs): user = current_user if user.is_staff: return view(*view_args, **view_kwargs) if not user.is_owner(kwargs['event_id']): return ForbiddenError({'source': ''}, 'Owner access is required').respond() return view(*view_args, **view_kwargs)
- Updated event schema to add new owner fields and relationship. We updated the fields –
- organizer_name -> owner_name
- has_organizer_info -> has_owner_info
- organizer_description -> owner_description
We also included owner relationship in the EventSchemaPublic
@use_defaults() class EventSchemaPublic(SoftDeletionSchema): ... owner_name = fields.Str(allow_none=True) has_owner_info = fields.Bool(default=False) owner_description = fields.Str(allow_none=True) ... owner = Relationship(attribute='owner', self_view='v1.event_owner', self_view_kwargs={'id': '<id>'}, related_view='v1.user_detail', schema='UserSchemaPublic', related_view_kwargs={'event_id': '<id>'}, type_='user')
- To accommodate the introduction of owner role, we have to introduce a new boolean field is_user_owner and a new relationship owner_events to the UserSchema. The relationship owner_events can be used to fetch lit of events of which a given user is the owner.
class UserSchema(UserSchemaPublic): ... is_user_owner = fields.Boolean(dump_only=True) ... owner_events = Relationship( self_view='v1.user_owner_event', self_view_kwargs={'id': '<id>'}, related_view='v1.event_list', schema='EventSchema', many=True, type_='event')
- Similarly, we need to update Event model too. A new owner relationship is introduced to the event model which is related to User. It basically stores the owner of the event.
We then introduce a new function get_owner( ) to the model which iterates through all the roles and return the user if the role is the owner.
class Event(SoftDeletionModel): ... owner = db.relationship('User', viewonly=True, secondary='join(UsersEventsRoles, Role, and_(Role.id == UsersEventsRoles.role_id, Role.name == "owner"))', primaryjoin='UsersEventsRoles.event_id == Event.id', secondaryjoin='User.id == UsersEventsRoles.user_id', backref='owner_events', uselist=False)
Resources:
Related work and code repo: