# This library is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this library; if not, see <http://www.gnu.org/licenses/>.
"""
Koji Smoky Dingo - CLI User Commands
:author: Christopher O'Brien <obriencj@gmail.com>
:license: GPL v3
"""
from koji import ClientSession
from operator import itemgetter
from typing import Optional, Union
from . import AnonSmokyDingo, int_or_str, pretty_json
from ..types import (
AuthType, TaskState, UserInfo, UserSpec, UserStatus, UserType, )
from ..users import (
collect_cgs, collect_perminfo, collect_userinfo, )
__all__ = (
"ShowCGInfo",
"ShowPermissionInfo",
"ShowUserInfo",
"cli_cginfo",
"cli_perminfo",
"cli_userinfo",
"get_userauth_str",
"get_userstatus_str",
"get_usertype_str",
)
[docs]
def get_usertype_str(userinfo: UserInfo) -> str:
"""
Provide a human-readable label for the koji user type enum value
in a koji user info dict.
:param userinfo: user info
:since: 1.0
"""
val = userinfo.get("usertype") or UserType.NORMAL
if val == UserType.NORMAL:
return "NORMAL (user)"
elif val == UserType.HOST:
return "HOST (builder)"
elif val == UserType.GROUP:
return "GROUP"
else:
return f"Unknown ({val})"
[docs]
def get_userstatus_str(userinfo: UserInfo) -> str:
"""
Provide a human-readable label for the koji user status enum value
in a koji user info dict.
:param userinfo: user info
:since: 1.0
"""
val = userinfo.get("status") or UserStatus.NORMAL
if val == UserStatus.NORMAL:
return "NORMAL (enabled)"
elif val == UserStatus.BLOCKED:
return "BLOCKED (disabled)"
else:
return f"Unknown ({val})"
[docs]
def get_userauth_str(userinfo: UserInfo) -> Optional[str]:
"""
Provide a human-readable label for the koji auth type enum value
in a koji user info dict. Returns None if the auth
:param userinfo: user info
:since: 2.0
"""
val = userinfo.get("authtype")
if val is None:
return None
elif val == AuthType.GSSAPI:
return "GSSAPI"
elif val == AuthType.KERB:
return "Kerberos ticket"
elif val == AuthType.NORMAL:
return "Password"
elif val == AuthType.SSL:
return "SSL certificate"
else:
return f"Unknown ({val})"
[docs]
def cli_userinfo(
session: ClientSession,
user: UserSpec,
stats: bool = False,
json: bool = False):
"""
Implements the ``koji userinfo`` command
:param session: an active koji client session
:param user: user specification to output information about
:param stats: include simple user stats
:param json: produce JSON output
:raises NoSuchUser: if the user specification doesn't correlate to
a known user
:since: 1.0
"""
userinfo = collect_userinfo(session, user, stats, members=True)
if json:
pretty_json(userinfo)
return
print(f"User: {userinfo['name']} [{userinfo['id']}]")
auth = get_userauth_str(userinfo)
if auth:
print("Authentication method:", auth)
krb_princs = userinfo.get("krb_principals", None)
if krb_princs:
print("Kerberos principals:")
for kp in sorted(krb_princs):
print(" ", kp)
print("Type:", get_usertype_str(userinfo))
print("Status:", get_userstatus_str(userinfo))
cgs = userinfo.get("content_generators", None)
if cgs:
print("Content generators:")
for cg in sorted(cgs, key=itemgetter("name")):
print(f"{cg['name']} [{cg['id']}]")
groups = userinfo.get("ksd_groups", None)
if groups:
print("Groups:")
for group in sorted(groups, key=lambda m: m.get("name")):
print(f" {group['name']} [{group['id']}]")
perms = userinfo.get("permissions", None)
if perms:
print("Permissions:")
for perm in sorted(perms):
print(" ", perm)
members = userinfo.get("ksd_members", None)
if members:
print("Members:")
for member in sorted(members, key=lambda m: m.get("name")):
print(f" {member['name']} [{member['id']}]")
data = userinfo.get("statistics", None)
if data:
print("Statistics:")
print(" Owned packages:", data.get("package_count", 0))
print(" Submitted tasks:", data.get("task_count", 0))
print(" Created builds:", data.get("build_count", 0))
tdat = data.get("last_task")
if tdat:
print(f" Last task: {tdat['method']} [{tdat['id']}]"
f" {tdat['create_time'].split('.')[0]}")
bdat = data.get("last_build")
if bdat:
print(f" Last build: {bdat['nvr']} [{bdat['build_id']}]"
f" {bdat['creation_time'].split('.')[0]}")
[docs]
class ShowUserInfo(AnonSmokyDingo):
group = "info"
description = "Show information about a user or group"
[docs]
def arguments(self, parser):
addarg = parser.add_argument
addarg("user", action="store", type=int_or_str, metavar="USER",
help="User name or principal")
addarg("--stats", action="store_true", default=False,
help="Include user statistics")
addarg("--json", action="store_true", default=False,
help="Output information as JSON")
return parser
[docs]
def handle(self, options):
return cli_userinfo(self.session, options.user,
stats=options.stats,
json=options.json)
[docs]
def cli_perminfo(
session: ClientSession,
permission: str,
verbose: bool = False,
by_date: bool = False,
json: bool = False):
"""
Implements the ``koji perminfo`` command
:param session: an active koji client session
:param permission: the permission name to display information about
:param verbose: also display who granted the permission and when
:param by_date: sort the user list by the date they were granted the
permission
:param json: output the data as JSON
:since: 1.0
"""
perminfo = collect_perminfo(session, permission)
if json:
pretty_json(perminfo)
return
print("Permission: {name} [{id}]".format(**perminfo))
users = perminfo["users"]
if users:
print("Users:")
if verbose:
fmt = " {user_name} [by {creator_name} on {create_date}]"
else:
fmt = " {user_name}"
orderkey = itemgetter("create_event" if by_date else "user_name")
for user in sorted(users, key=orderkey):
print(fmt.format(**user))
[docs]
class ShowPermissionInfo(AnonSmokyDingo):
description = "Show information about a permission"
[docs]
def arguments(self, parser):
addarg = parser.add_argument
addarg("permission", action="store", metavar="PERMISSION",
type=int_or_str,
help="Name of permission")
addarg("--verbose", "-v", action="store_true", default=False,
help="Also show who granted the permission and when")
addarg("--by-date", "-d", action="store_true", default=False,
help="Sory users by date granted. Otherwise, sort by name")
addarg("--json", action="store_true", default=False,
help="Output information as JSON")
return parser
[docs]
def handle(self, options):
return cli_perminfo(self.session, options.permission,
verbose=options.verbose,
by_date=options.by_date,
json=options.json)
[docs]
def cli_cginfo(
session: ClientSession,
name: Optional[str] = None,
json: bool = False):
"""
Implements the ``koji cginfo`` command
:param session: an active koji client session
:param name: only display information about the content generator with
this name
:param json: output the data as JSON
:since: 1.0
"""
cgs = collect_cgs(session, name=name)
if json:
pretty_json(cgs)
return
for cginfo in sorted(cgs, key=itemgetter("id")):
print("Content generator: {name} [{id}]".format(**cginfo))
users = cginfo.get("users")
if users:
print("Users:")
for user in sorted(users):
print(" ", user)
print()
[docs]
class ShowCGInfo(AnonSmokyDingo):
description = "List content generators and their users"
[docs]
def arguments(self, parser):
addarg = parser.add_argument
addarg("--name", action="store", default=None,
help="Only show the given content generator")
addarg("--json", action="store_true", default=False,
help="Output information as JSON")
return parser
[docs]
def handle(self, options):
return cli_cginfo(self.session,
name=options.name,
json=options.json)
#
# The end.