Support for ACLs
Users in the "admin" level can set aliases, rebuild packages, and trigger services. On the other hand, users in the "user" level can only perform read-only operations (reading status at this point).
This commit is contained in:
parent
aea53325ad
commit
c9d4e0312d
2 changed files with 49 additions and 2 deletions
|
@ -34,3 +34,8 @@ repo_aliases:
|
||||||
repository: "KDE_Unstable_Frameworks_openSUSE_Factory"
|
repository: "KDE_Unstable_Frameworks_openSUSE_Factory"
|
||||||
state: "failed"
|
state: "failed"
|
||||||
arch: "all"
|
arch: "all"
|
||||||
|
acl:
|
||||||
|
admin:
|
||||||
|
- "@CHANGE_ME"
|
||||||
|
user: []
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,7 @@ class Config(BaseProxyConfig):
|
||||||
|
|
||||||
helper.copy("username")
|
helper.copy("username")
|
||||||
helper.copy("repo_aliases")
|
helper.copy("repo_aliases")
|
||||||
|
helper.copy("acl")
|
||||||
|
|
||||||
|
|
||||||
class OSCBot(Plugin):
|
class OSCBot(Plugin):
|
||||||
|
@ -114,6 +115,18 @@ class OSCBot(Plugin):
|
||||||
|
|
||||||
return (project, package, repository, state, arch)
|
return (project, package, repository, state, arch)
|
||||||
|
|
||||||
|
def check_acl(self, user_id: str, need_admin: bool = False) -> bool:
|
||||||
|
|
||||||
|
acls = self.config["acl"]
|
||||||
|
if not need_admin:
|
||||||
|
if user_id in acls["admin"] or acls["user"]:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
if user_id in acls["admin"]:
|
||||||
|
return True
|
||||||
|
self.log.debug(f"Denied operation as {user_id}")
|
||||||
|
return False
|
||||||
|
|
||||||
async def parse_rebuilpac(
|
async def parse_rebuilpac(
|
||||||
self,
|
self,
|
||||||
project: str,
|
project: str,
|
||||||
|
@ -222,6 +235,10 @@ class OSCBot(Plugin):
|
||||||
@alias.subcommand("list", help="List configured aliases")
|
@alias.subcommand("list", help="List configured aliases")
|
||||||
async def list_aliases(self, evt: MessageEvent) -> None:
|
async def list_aliases(self, evt: MessageEvent) -> None:
|
||||||
|
|
||||||
|
if not self.check_acl(evt.sender, need_admin=False):
|
||||||
|
await evt.reply("You are not authorized to perform this action.")
|
||||||
|
return
|
||||||
|
|
||||||
body = self.template.from_string(ALIAS_TEMPLATE)
|
body = self.template.from_string(ALIAS_TEMPLATE)
|
||||||
|
|
||||||
if self.config.get("repo_aliases", None) is None:
|
if self.config.get("repo_aliases", None) is None:
|
||||||
|
@ -246,6 +263,10 @@ class OSCBot(Plugin):
|
||||||
arch: Optional[str] = None,
|
arch: Optional[str] = None,
|
||||||
state: Optional[str] = None) -> None:
|
state: Optional[str] = None) -> None:
|
||||||
|
|
||||||
|
if not self.check_acl(evt.sender, need_admin=True):
|
||||||
|
await evt.reply("You are not authorized to perform this action.")
|
||||||
|
return
|
||||||
|
|
||||||
if alias not in self.config["repo_aliases"]:
|
if alias not in self.config["repo_aliases"]:
|
||||||
await evt.respond(f"Unknown alias {alias}")
|
await evt.respond(f"Unknown alias {alias}")
|
||||||
return
|
return
|
||||||
|
@ -286,6 +307,10 @@ class OSCBot(Plugin):
|
||||||
arch: Optional[str] = None,
|
arch: Optional[str] = None,
|
||||||
state: Optional[str] = None) -> None:
|
state: Optional[str] = None) -> None:
|
||||||
|
|
||||||
|
if not self.check_acl(evt.sender, need_admin=True):
|
||||||
|
await evt.reply("You are not authorized to perform this action.")
|
||||||
|
return
|
||||||
|
|
||||||
repository = "all" if not repository else repository
|
repository = "all" if not repository else repository
|
||||||
package = "all" if not package else package
|
package = "all" if not package else package
|
||||||
arch = "all" if not arch else arch
|
arch = "all" if not arch else arch
|
||||||
|
@ -304,6 +329,11 @@ class OSCBot(Plugin):
|
||||||
@alias.subcommand("delete", help="Delete an alias", aliases=("rm", ))
|
@alias.subcommand("delete", help="Delete an alias", aliases=("rm", ))
|
||||||
@command.argument("alias", "alias name")
|
@command.argument("alias", "alias name")
|
||||||
async def delete_alias(self, evt: MessageEvent, alias: str):
|
async def delete_alias(self, evt: MessageEvent, alias: str):
|
||||||
|
|
||||||
|
if not self.check_acl(evt.sender, need_admin=True):
|
||||||
|
await evt.reply("You are not authorized to perform this action.")
|
||||||
|
return
|
||||||
|
|
||||||
if alias not in self.config["repo_aliases"]:
|
if alias not in self.config["repo_aliases"]:
|
||||||
await evt.respond(f"Unknown alias {alias}")
|
await evt.respond(f"Unknown alias {alias}")
|
||||||
return
|
return
|
||||||
|
@ -325,6 +355,10 @@ class OSCBot(Plugin):
|
||||||
repository: Optional[str] = None,
|
repository: Optional[str] = None,
|
||||||
arch: Optional[str] = None) -> None:
|
arch: Optional[str] = None) -> None:
|
||||||
|
|
||||||
|
if not self.check_acl(evt.sender, need_admin=True):
|
||||||
|
await evt.reply("You are not authorized to perform this action.")
|
||||||
|
return
|
||||||
|
|
||||||
if project in self.config["repo_aliases"]:
|
if project in self.config["repo_aliases"]:
|
||||||
# We're not interested in state and we query package explicitly
|
# We're not interested in state and we query package explicitly
|
||||||
project, _, repository, _, arch = self.get_alias(project)
|
project, _, repository, _, arch = self.get_alias(project)
|
||||||
|
@ -353,6 +387,10 @@ class OSCBot(Plugin):
|
||||||
project: str,
|
project: str,
|
||||||
package: str) -> None:
|
package: str) -> None:
|
||||||
|
|
||||||
|
if not self.check_acl(evt.sender, need_admin=True):
|
||||||
|
await evt.reply("You are not authorized to perform this action.")
|
||||||
|
return
|
||||||
|
|
||||||
token = self.config["trigger_token"]
|
token = self.config["trigger_token"]
|
||||||
trigger_url = f"{self.config['api_url']}/trigger/runservice"
|
trigger_url = f"{self.config['api_url']}/trigger/runservice"
|
||||||
params = {"project": project, "package": package}
|
params = {"project": project, "package": package}
|
||||||
|
@ -382,6 +420,10 @@ class OSCBot(Plugin):
|
||||||
repository: Optional[str] = None,
|
repository: Optional[str] = None,
|
||||||
arch: Optional[str] = None) -> None:
|
arch: Optional[str] = None) -> None:
|
||||||
|
|
||||||
|
if not self.check_acl(evt.sender, need_admin=False):
|
||||||
|
await evt.reply("You are not authorized to perform this action.")
|
||||||
|
return
|
||||||
|
|
||||||
if project in self.config["repo_aliases"]:
|
if project in self.config["repo_aliases"]:
|
||||||
project, package, repository, state, arch = self.get_alias(project)
|
project, package, repository, state, arch = self.get_alias(project)
|
||||||
else:
|
else:
|
||||||
|
@ -428,5 +470,5 @@ class OSCBot(Plugin):
|
||||||
message = body.render(packages=packagelist, base_url=base_url,
|
message = body.render(packages=packagelist, base_url=base_url,
|
||||||
project=project, repo=repository)
|
project=project, repo=repository)
|
||||||
await evt.respond(message, markdown=True)
|
await evt.respond(message, markdown=True)
|
||||||
# Wait 100 milliseconds to avoid triggering rate limiting
|
# Wait 200 milliseconds to avoid triggering rate limiting
|
||||||
sleep(0.1)
|
sleep(0.2)
|
||||||
|
|
Loading…
Add table
Reference in a new issue