diff --git a/mo/web/org_contest.py b/mo/web/org_contest.py
index 26df8205bc38c3ae7d73b19af1c8e4ee5f2c38c5..083eefb84ca324ff0892fbafaa4ac920ca634a82 100644
--- a/mo/web/org_contest.py
+++ b/mo/web/org_contest.py
@@ -16,7 +16,6 @@ from mo.csv import FileFormat
 import mo.db as db
 from mo.imports import ImportType, create_import
 import mo.jobs.submit
-import mo.rights
 from mo.rights import Right, Rights
 import mo.util
 from mo.util_format import inflect_number
@@ -78,11 +77,9 @@ class ParticipantsActionForm(FlaskForm):
 
     remove_participation = wtforms.SubmitField("Smazat záznam o účasti")
 
-    def do_action(self, round: db.Round, rights: Rights, query: Query) -> bool:
+    def do_action(self, round: db.Round, query: Query) -> bool:
         """Do participation modification on partipations from given query
-        (possibly filtered by checkboxes). `rights` param is used to check rights
-        for contest of each modified participation or for contest in which
-        participation is moved to."""
+        (possibly filtered by checkboxes)."""
 
         if not self.validate_on_submit():
             return False
@@ -106,8 +103,8 @@ class ParticipantsActionForm(FlaskForm):
             if not contest:
                 flash(f"Nepovedlo se najít soutěž v kole {round.round_code()} v oblasti {contest_place.name}", 'danger')
                 return False
-            rights.get_for_contest(contest)
-            if not rights.have_right(Right.manage_contest):
+            rr = g.gatekeeper.rights_for_contest(contest)
+            if not rr.have_right(Right.manage_contest):
                 flash(f"Nemáte právo ke správě soutěže v kole {round.round_code()} v oblasti {contest_place.name}, nelze do ní přesunout účastníky", 'danger')
                 return False
         elif self.remove_participation.data:
@@ -124,22 +121,17 @@ class ParticipantsActionForm(FlaskForm):
 
         # Check all participations if we can edit them
         ctants = query.all()
-        rights_cache = set()
         for pion, _, _ in ctants:
             u = pion.user
             if self.action_on.data == 'checked' and u.user_id not in user_ids:
                 continue
-            if pion.contest_id in rights_cache:
-                continue
-            rights.get_for_contest(pion.contest)
-            if rights.have_right(Right.manage_contest):
-                rights_cache.add(pion.contest_id)
-                continue
-            flash(
-                f"Nemáte právo ke správě soutěže v kole {round.round_code()} v oblasti {pion.contest.place.name} "
-                + f"(účastník {u.full_name()}). Žádná akce nebyla provedena.", 'danger'
-            )
-            return False
+            rr = g.gatekeeper.rights_for_contest(pion.contest)
+            if not rr.have_right(Right.manage_contest):
+                flash(
+                    f"Nemáte právo ke správě soutěže v kole {round.round_code()} v oblasti {pion.contest.place.name} "
+                    + f"(účastník {u.full_name()}). Žádná akce nebyla provedena.", 'danger'
+                )
+                return False
 
         count = 0
         for pion, _, _ in ctants:
@@ -206,8 +198,7 @@ def get_contest(id: int) -> db.Contest:
 def get_contest_rr(id: int, right_needed: Optional[Right] = None) -> Tuple[db.Contest, Rights]:
     contest = get_contest(id)
 
-    rr = Rights(g.user)
-    rr.get_for_contest(contest)
+    rr = g.gatekeeper.rights_for_contest(contest)
 
     if not (right_needed is None or rr.have_right(right_needed)):
         raise werkzeug.exceptions.Forbidden()
@@ -215,7 +206,7 @@ def get_contest_rr(id: int, right_needed: Optional[Right] = None) -> Tuple[db.Co
     return contest, rr
 
 
-def get_contest_site_rr(id: int, site_id: Optional[int], right_needed: Optional[Right] = None) -> Tuple[db.Contest, db.Place, Rights]:
+def get_contest_site_rr(id: int, site_id: Optional[int], right_needed: Optional[Right] = None) -> Tuple[db.Contest, Optional[db.Place], Rights]:
     if site_id is None:
         contest, rr = get_contest_rr(id, right_needed)
         return contest, None, rr
@@ -225,8 +216,7 @@ def get_contest_site_rr(id: int, site_id: Optional[int], right_needed: Optional[
     if not site:
         raise werkzeug.exceptions.NotFound()
 
-    rr = Rights(g.user)
-    rr.get_for_contest_site(contest, site)
+    rr = g.gatekeeper.rights_for_contest_site(contest, site)
 
     if not (right_needed is None or rr.have_right(right_needed)):
         raise werkzeug.exceptions.Forbidden()
@@ -274,7 +264,7 @@ def org_contest(id: int, site_id: Optional[int] = None):
         'org_contest.html',
         contest=contest, site=site,
         db=db,  # kvůli hodnotám enumů
-        rights=sorted(rr.current_rights, key=lambda r: r. name),
+        rights=sorted(rr.rights, key=lambda r: r. name),
         can_manage=rr.have_right(Right.manage_contest),
         can_edit_points=rr.have_right(Right.edit_points) and contest.round.state == db.RoundState.grading,
         tasks=tasks, places_counts=places_counts,
@@ -355,7 +345,7 @@ def org_contest_list(id: int, site_id: Optional[int] = None):
     action_form = None
     if can_edit:
         action_form = ParticipantsActionForm()
-        if action_form.do_action(round=contest.round, rights=rr, query=query):
+        if action_form.do_action(round=contest.round, query=query):
             # Action happened, redirect
             return redirect(request.url)
 
@@ -518,8 +508,7 @@ def get_solution_context(contest_id: int, user_id: Optional[int], task_id: Optio
             if not site:
                 raise werkzeug.exceptions.NotFound()
 
-    rr = Rights(g.user)
-    rr.get_for_contest_site(contest, site or contest.place)
+    rr = g.gatekeeper.rights_for_contest_site(contest, site or contest.place)
 
     # Kdo má právo na jaké operace
     allow_upload_solutions = (rr.have_right(Right.upload_submits)
diff --git a/mo/web/org_round.py b/mo/web/org_round.py
index d157837449c5a0f01e71ee295304caad22f636e3..3780150f738f218ef912ce97bce355f2f5c80cb9 100644
--- a/mo/web/org_round.py
+++ b/mo/web/org_round.py
@@ -1,8 +1,6 @@
 from typing import Optional, Tuple
 from flask import render_template, g, redirect, url_for, flash, request
 import locale
-import os
-import secrets
 from flask_wtf.form import FlaskForm
 from sqlalchemy import func
 from sqlalchemy.orm import joinedload
@@ -14,7 +12,7 @@ from wtforms import validators
 import mo
 import mo.db as db
 import mo.imports
-import mo.rights
+from mo.rights import Right, Rights
 import mo.util
 from mo.web import app
 from mo.web.org_contest import ParticipantsActionForm, ParticipantsFilterForm, get_contestants_query, make_contestant_table, generic_import
@@ -27,11 +25,10 @@ def get_round(id: int) -> db.Round:
     return round
 
 
-def get_round_rr(id: int, right_needed: Optional[mo.rights.Right]) -> Tuple[db.Round, mo.rights.Rights]:
+def get_round_rr(id: int, right_needed: Optional[Right]) -> Tuple[db.Round, Rights]:
     round = get_round(id)
 
-    rr = mo.rights.Rights(g.user)
-    rr.get_for_round(round)
+    rr = g.gatekeeper.rights_for_round(round)
 
     if not (right_needed is None or rr.have_right(right_needed)):
         raise werkzeug.exceptions.Forbidden()
@@ -57,8 +54,8 @@ def org_round(id: int):
     sess = db.get_session()
     round, rr = get_round_rr(id, None)
 
-    can_manage_round = rr.have_right(mo.rights.Right.manage_round)
-    can_manage_contestants = rr.have_right(mo.rights.Right.manage_contest)
+    can_manage_round = rr.have_right(Right.manage_round)
+    can_manage_contestants = rr.have_right(Right.manage_contest)
 
     participants_count = sess.query(
         db.Participation.contest_id,
@@ -136,7 +133,7 @@ class TaskEditForm(FlaskForm):
 @app.route('/org/contest/r/<int:id>/task/new', methods=('GET', 'POST'))
 def org_round_task_new(id: int):
     sess = db.get_session()
-    round, rr = get_round_rr(id, mo.rights.Right.manage_round)
+    round, rr = get_round_rr(id, Right.manage_round)
 
     form = TaskEditForm()
     if form.validate_on_submit():
@@ -168,7 +165,7 @@ def org_round_task_new(id: int):
 @app.route('/org/contest/r/<int:id>/task/<int:task_id>/edit', methods=('GET', 'POST'))
 def org_round_task_edit(id: int, task_id: int):
     sess = db.get_session()
-    round, rr = get_round_rr(id, mo.rights.Right.manage_round)
+    round, rr = get_round_rr(id, Right.manage_round)
 
     task = sess.query(db.Task).get(task_id)
     if not task:
@@ -206,7 +203,7 @@ def org_round_task_edit(id: int, task_id: int):
 
 @app.route('/org/contest/r/<int:id>/list', methods=('GET', 'POST'))
 def org_round_list(id: int):
-    round, rr = get_round_rr(id, mo.rights.Right.manage_contest)
+    round, rr = get_round_rr(id, Right.manage_contest)
     format = request.args.get('format', "")
 
     filter = ParticipantsFilterForm(request.args)
@@ -220,7 +217,7 @@ def org_round_list(id: int):
     )
 
     action_form = ParticipantsActionForm()
-    if action_form.do_action(round=round, rights=rr, query=query):
+    if action_form.do_action(round=round, query=query):
         # Action happened, redirect
         return redirect(request.url)
 
@@ -242,7 +239,7 @@ def org_round_list(id: int):
 
 @app.route('/org/contest/r/<int:id>/import', methods=('GET', 'POST'))
 def org_round_import(id: int):
-    round, rr = get_round_rr(id, mo.rights.Right.manage_contest)
+    round, rr = get_round_rr(id, Right.manage_contest)
     return generic_import(round, None)
 
 
@@ -269,7 +266,7 @@ class RoundEditForm(FlaskForm):
 @app.route('/org/contest/r/<int:id>/edit', methods=('GET', 'POST'))
 def org_round_edit(id: int):
     sess = db.get_session()
-    round, rr = get_round_rr(id, mo.rights.Right.manage_round)
+    round, rr = get_round_rr(id, Right.manage_round)
 
     form = RoundEditForm(obj=round)
     if form.validate_on_submit():