Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New User Model #448

Merged
merged 59 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
1aae05f
New User Model
TheBurchLog Feb 20, 2024
504d423
code cleanup
TheBurchLog Feb 20, 2024
7705412
Expanding to support remote user mappings
TheBurchLog Feb 20, 2024
df98c7b
Merge remote-tracking branch 'origin/develop' into User_Model
TheBurchLog Feb 21, 2024
13114cc
Replace Refresh with User Token
TheBurchLog Feb 21, 2024
c3942df
fixed typo
TheBurchLog Feb 28, 2024
bb6e025
model updates
TheBurchLog Feb 28, 2024
c660498
Add Local Roles to users
TheBurchLog Feb 28, 2024
eb5ddc1
Cleaning up models
TheBurchLog Mar 8, 2024
fbbedb6
Update fixtures.py
TheBurchLog Mar 8, 2024
9aeb929
added back shared users
TheBurchLog Mar 8, 2024
ddd06fd
updated schema
TheBurchLog Mar 8, 2024
69c6eea
Updating Admin roles
TheBurchLog Mar 11, 2024
f7bd06f
updating fixtures
TheBurchLog Mar 11, 2024
af9e8a6
updating fixtures
TheBurchLog Mar 11, 2024
da29301
fixed testing
TheBurchLog Mar 12, 2024
6296b72
UserToken update
TheBurchLog Mar 13, 2024
7c49af0
Updated UserToken
TheBurchLog Mar 13, 2024
f84eead
Fixed model
TheBurchLog Mar 13, 2024
6f0cbee
Fixed Models
TheBurchLog Mar 13, 2024
14d0276
Fixed model
TheBurchLog Mar 13, 2024
11a4772
User ID field
TheBurchLog Mar 13, 2024
562f8b7
fixed user id
TheBurchLog Mar 13, 2024
d26a394
All none on Users
TheBurchLog Mar 20, 2024
9e7be70
Usertoken stores username
TheBurchLog Mar 20, 2024
02d0d12
Allow none on roles
TheBurchLog Mar 20, 2024
e1f44d7
Adding Remote Roles
TheBurchLog Mar 20, 2024
2826697
Fixed schemas
TheBurchLog Mar 21, 2024
181557b
Add Remote Role Schema parsing
TheBurchLog Mar 21, 2024
e2b2929
Merge branch 'develop' into User_Model
TheBurchLog Apr 1, 2024
e591cd6
Updating testing
TheBurchLog Apr 1, 2024
121d7c4
Updating more testing
TheBurchLog Apr 1, 2024
3503afd
combining roles schemas
TheBurchLog Apr 1, 2024
ba4fabb
Merge remote-tracking branch 'origin/develop' into User_Model
TheBurchLog Apr 17, 2024
ed93199
Add protected to roles
TheBurchLog Apr 18, 2024
265b76a
Merge branch 'develop' into User_Model
TheBurchLog May 20, 2024
014cba4
Adding is protected to user accounts
TheBurchLog Jun 3, 2024
3865bd7
Changed is_protected to protected
TheBurchLog Jun 3, 2024
4aae1dc
fixed model
TheBurchLog Jun 3, 2024
198380b
Update Requester on child requests
TheBurchLog Jun 3, 2024
f31dc46
Set requester from parent
TheBurchLog Jun 3, 2024
5fb7344
add file generated flag
TheBurchLog Jun 4, 2024
fdb17e3
Load Tokens first if configured
TheBurchLog Jun 5, 2024
f969178
Merge branch 'develop' into User_Model
TheBurchLog Jun 10, 2024
3949c61
User metadata defaults
TheBurchLog Jun 11, 2024
64f81e3
changing to upstream and alias
TheBurchLog Jun 12, 2024
8c961d2
fixing testing
TheBurchLog Jun 12, 2024
ce072b2
formatting
TheBurchLog Jun 12, 2024
4daaf21
formatting
TheBurchLog Jun 12, 2024
c97d4ce
Roles Equals func
TheBurchLog Jun 13, 2024
92aa210
Update Events Roles
TheBurchLog Jun 13, 2024
d114217
User Model Equals
TheBurchLog Jun 14, 2024
cd47aad
Formatting
TheBurchLog Jun 19, 2024
0786812
Change Log Updates
TheBurchLog Jun 19, 2024
c5c8521
update to user_alias_mapping
TheBurchLog Jun 21, 2024
adb189b
PR Requested Changes
TheBurchLog Jun 26, 2024
350474e
Add Permissions Enum
TheBurchLog Jun 26, 2024
b78ee1f
Linting
TheBurchLog Jun 28, 2024
a24b72a
Merge branch 'develop' into User_Model
TheBurchLog Jul 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
Brewtils Changelog
==================

3.27.0
------
TBD

- New Models for User, UserToken, Role, and AliasUserMap
- Must upgrade to a minimum version of Beer Garden 3.27.0 to support new authentication models. If authentication is not enabled, upgrade
is not required.
- Removed 2.0 Legacy support for Principle and LegacyRole models
- Fixed bug in SystemClient to properly assign requester field from parent request

3.26.4
------
7/12/24
Expand Down
1 change: 0 additions & 1 deletion brewtils/auto_decorator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from inspect import Parameter as InspectParameter # noqa
from inspect import signature


from brewtils.models import Command, Parameter


Expand Down
237 changes: 172 additions & 65 deletions brewtils/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@
"Event",
"Events",
"Queue",
"Principal",
"LegacyRole",
"RefreshToken",
"UserToken",
"Job",
"RequestFile",
"File",
Expand All @@ -37,6 +35,8 @@
"Garden",
"Operation",
"Resolvable",
"Role",
"User",
"Subscriber",
"Topic",
]
Expand Down Expand Up @@ -92,7 +92,7 @@ class Events(Enum):
USER_UPDATED = 44
USERS_IMPORTED = 45
ROLE_UPDATED = 46
ROLES_IMPORTED = 47
ROLE_DELETED = 47
COMMAND_PUBLISHING_BLOCKLIST_SYNC = 48
COMMAND_PUBLISHING_BLOCKLIST_REMOVE = 49
COMMAND_PUBLISHING_BLOCKLIST_UPDATE = 50
Expand All @@ -103,6 +103,13 @@ class Events(Enum):
# Next: 57


class Permissions(Enum):
READ_ONLY = 1
OPERATOR = 2
PLUGIN_ADMIN = 3
GARDEN_ADMIN = 4


class BaseModel(object):
schema = None

Expand Down Expand Up @@ -1110,81 +1117,32 @@ def __repr__(self):
return "<Queue: name=%s, size=%s>" % (self.name, self.size)


class Principal(BaseModel):
schema = "PrincipalSchema"
class UserToken(BaseModel):
schema = "UserTokenSchema"

def __init__(
self,
id=None, # noqa # shadows built-in
uuid=None,
issued_at=None,
expires_at=None,
username=None,
roles=None,
permissions=None,
preferences=None,
metadata=None,
):
self.id = id
self.uuid = uuid
self.issued_at = issued_at
self.expires_at = expires_at
self.username = username
self.roles = roles
self.permissions = permissions
self.preferences = preferences
self.metadata = metadata

def __str__(self):
return "%s" % self.username

def __repr__(self):
return "<Principal: username=%s, roles=%s, permissions=%s>" % (
return "<UserToken: uuid=%s, issued_at=%s, expires_at=%s, username=%s>" % (
self.uuid,
self.issued_at,
self.expires_at,
self.username,
self.roles,
self.permissions,
)


class LegacyRole(BaseModel):
schema = "LegacyRoleSchema"

def __init__(
self,
id=None, # noqa # shadows built-in
name=None,
description=None,
permissions=None,
):
self.id = id
self.name = name
self.description = description
self.permissions = permissions

def __str__(self):
return "%s" % self.name

def __repr__(self):
return "<LegacyRole: name=%s, permissions=%s>" % (self.name, self.permissions)


class RefreshToken(BaseModel):
schema = "RefreshTokenSchema"

def __init__(
self,
id=None, # noqa # shadows built-in
issued=None,
expires=None,
payload=None,
):
self.id = id
self.issued = issued
self.expires = expires
self.payload = payload or {}

def __str__(self):
return "%s" % self.payload

def __repr__(self):
return "<RefreshToken: issued=%s, expires=%s, payload=%s>" % (
self.issued,
self.expires,
self.payload,
)


Expand Down Expand Up @@ -1471,6 +1429,8 @@ def __init__(
parent=None,
children=None,
metadata=None,
default_user=None,
shared_users=None,
):
self.id = id
self.name = name
Expand All @@ -1488,6 +1448,9 @@ def __init__(
self.children = children
self.metadata = metadata or {}

self.default_user = default_user
self.shared_users = shared_users

def __str__(self):
return "%s" % self.name

Expand Down Expand Up @@ -1658,6 +1621,150 @@ def __repr__(self):
)


class User(BaseModel):
schema = "UserSchema"

def __init__(
self,
username=None,
id=None,
password=None,
roles=None,
local_roles=None,
upstream_roles=None,
user_alias_mapping=None,
metadata=None,
is_remote=False,
protected=False,
file_generated=False,
):
self.username = username
TheBurchLog marked this conversation as resolved.
Show resolved Hide resolved
self.id = id
self.password = password
self.roles = roles or []
self.local_roles = local_roles or []
self.upstream_roles = upstream_roles or []
self.is_remote = is_remote
self.user_alias_mapping = user_alias_mapping or []
self.metadata = metadata or {}
self.protected = protected
self.file_generated = file_generated

def __str__(self):
return "%s: %s" % (self.username, self.roles)

def __repr__(self):
return "<User: username=%s, roles=%s>" % (
self.username,
self.roles,
)

def __eq__(self, other):
if not isinstance(other, User):
# don't attempt to compare against unrelated types
return NotImplemented

return (
self.username == other.username
and self.roles == other.roles
and self.upstream_roles == other.upstream_roles
and self.is_remote == other.is_remote
and self.user_alias_mapping == other.user_alias_mapping
and self.protected == other.protected
and self.file_generated == other.file_generated
)


class Role(BaseModel):
schema = "RoleSchema"

# TODO: REMOVE after DB model Updated with Permissions enum
PERMISSION_TYPES = {
"GARDEN_ADMIN",
"PLUGIN_ADMIN",
"OPERATOR",
"READ_ONLY", # Default value if not role is provided
}

def __init__(
self,
name,
permission=None,
description=None,
id=None,
scope_gardens=None,
scope_namespaces=None,
scope_systems=None,
scope_instances=None,
scope_versions=None,
scope_commands=None,
protected=False,
file_generated=False,
):
self.name = name
self.permission = permission or Permissions.READ_ONLY.name
self.description = description
self.id = id
self.scope_gardens = scope_gardens or []
self.scope_namespaces = scope_namespaces or []
self.scope_systems = scope_systems or []
self.scope_instances = scope_instances or []
self.scope_versions = scope_versions or []
self.scope_commands = scope_commands or []
self.protected = protected
self.file_generated = file_generated

def __str__(self):
return "%s" % (self.name)

def __repr__(self):
return (
"<Role: id=%s, name=%s, description=%s, permission=%s, scope_garden=%s, "
"scope_namespaces=%s, scope_systems=%s, scope_instances=%s, "
"scope_versions=%s, scope_commands=%s>"
) % (
self.id,
self.name,
self.description,
self.permission,
self.scope_gardens,
self.scope_namespaces,
self.scope_systems,
self.scope_instances,
self.scope_versions,
self.scope_commands,
)

def __eq__(self, other):
if not isinstance(other, Role):
# don't attempt to compare against unrelated types
return NotImplemented

return (
self.name == other.name
TheBurchLog marked this conversation as resolved.
Show resolved Hide resolved
and self.description == other.description
and self.permission == other.permission
and self.scope_gardens == other.scope_gardens
and self.scope_namespaces == other.scope_namespaces
and self.scope_systems == other.scope_systems
and self.scope_instances == other.scope_instances
and self.scope_versions == other.scope_versions
and self.scope_commands == other.scope_commands
)


class UpstreamRole(Role):
schema = "UpstreamRoleSchema"


class AliasUserMap(BaseModel):
schema = "AliasUserMapSchema"

def __init__(self, target_garden, username):
self.target_garden = target_garden
self.username = username


class Subscriber(BaseModel):
schema = "SubscriberSchema"

Expand Down
3 changes: 2 additions & 1 deletion brewtils/request_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import six
from requests import ConnectionError as RequestsConnectionError

from brewtils.decorators import _parse_method
import brewtils.plugin
from brewtils.decorators import _parse_method
from brewtils.errors import (
BGGivesUpError,
DiscardMessageException,
Expand Down Expand Up @@ -61,6 +61,7 @@ def process_command(self, request):

if parent_request:
request.parent = Request(id=str(parent_request.id))
request.requester = parent_request.requester
request.has_parent = True

# check for kwargs on the target command
Expand Down
9 changes: 8 additions & 1 deletion brewtils/rest/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,19 @@ def enable_auth(method):

@functools.wraps(method)
def wrapper(self, *args, **kwargs):

# Load Token initially if authentication settings are provided
if not self.session.headers.get("Authorization") and (
(self.username and self.password) or self.client_cert
):
self.get_tokens()

original_response = method(self, *args, **kwargs)

if original_response.status_code != 401:
return original_response

# Try to use credentials
# Refresh Token if expired and caused 401
if (self.username and self.password) or self.client_cert:
credential_response = self.get_tokens()

Expand Down
8 changes: 3 additions & 5 deletions brewtils/rest/easy_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1089,17 +1089,15 @@ def forward(self, operation, **kwargs):
SchemaParser.serialize_operation(operation), **kwargs
)

@wrap_response(
parse_method="parse_principal", parse_many=False, default_exc=FetchError
)
@wrap_response(parse_method="parse_user", parse_many=False, default_exc=FetchError)
def get_user(self, user_identifier):
"""Find a user

Args:
user_identifier (str): User ID or username

Returns:
Principal: The User
User: The User

"""
return self.client.get_user(user_identifier)
Expand All @@ -1108,7 +1106,7 @@ def who_am_i(self):
"""Find user using the current set of credentials

Returns:
Principal: The User
User: The User

"""
return self.get_user(self.client.username or "anonymous")
Expand Down
Loading
Loading