from __future__ import annotations
from enum import Enum
from typing import Optional, Union, List
try:
from pydantic.v1 import BaseModel, Field, validator, constr, Extra
except ImportError:
from pydantic import BaseModel, Field, validator, constr, Extra
from ..exceptions import InvalidPasswordError, InvalidUsernameError, InvalidRolenameError, InvalidGroupnameError
[docs]
class AuthTypeEnum(str, Enum):
password = "password"
[docs]
def is_valid_password(password: str) -> None:
# Null character not allowed
if "\x00" in password:
raise InvalidPasswordError("Password contains a NUL character")
# Password should be somewhat long
if len(password) == 0:
raise InvalidPasswordError("Password is empty")
if len(password) < 6:
raise InvalidPasswordError("Password must contain at least 6 characters")
[docs]
def is_valid_username(username: str) -> None:
if len(username) == 0:
raise InvalidUsernameError("Username is empty")
# Null character not allowed
if "\x00" in username:
raise InvalidUsernameError("Username contains a NUL character")
# Spaces are not allowed
if " " in username:
raise InvalidUsernameError("Username contains spaces")
# Username cannot be all numbers
if username.isnumeric():
raise InvalidUsernameError("Username cannot be all numbers")
[docs]
def is_valid_groupname(groupname: str) -> None:
if len(groupname) == 0:
raise InvalidGroupnameError("Groupname is empty")
# Null character not allowed
if "\x00" in groupname:
raise InvalidGroupnameError("Groupname contains a NUL character")
# Spaces are not allowed
if " " in groupname:
raise InvalidGroupnameError("Groupname contains spaces")
# Groupname cannot be all numbers
if groupname.isnumeric():
raise InvalidGroupnameError("Groupname cannot be all numbers")
[docs]
def is_valid_rolename(rolename: str) -> None:
if len(rolename) == 0:
raise InvalidRolenameError("Rolename is empty")
# Null character not allowed
if "\x00" in rolename:
raise InvalidRolenameError("Rolename contains a NUL character")
# Spaces are not allowed
if " " in rolename:
raise InvalidRolenameError("Rolename contains spaces")
# Rolename cannot be all numbers
if rolename.isnumeric():
raise InvalidRolenameError("Rolename cannot be all numbers")
[docs]
class PolicyStatement(BaseModel):
"""
A statment of permissions for a role
"""
Effect: str = Field(..., description="The effect of the permission (Allow, Deny)")
Action: Union[str, List[str]] = Field(
..., description="The actions this permission applies to (GET, POST, etc). May be '*' to apply to all."
)
Resource: Union[str, List[str]] = Field(
...,
description="The resource this permission applies to. Usually the first part of the path (molecules, "
"keywords, etc). May be '*' to apply to all.",
)
[docs]
class PermissionsPolicy(BaseModel):
"""
Permissions assigned to a role
"""
class Config:
extra = Extra.forbid
Statement: List[PolicyStatement] = Field(..., description="Permission statements")
[docs]
class RoleInfo(BaseModel):
"""
Information about a role
"""
class Config:
extra = Extra.forbid
rolename: str = Field(..., description="The name of the role")
permissions: PermissionsPolicy = Field(..., description="The permissions associated with this role")
@validator("rolename", pre=True)
def _valid_rolename(cls, v):
"""Makes sure the username is a valid string"""
try:
is_valid_rolename(v)
return v
except Exception as e:
raise ValueError(str(e))
[docs]
class GroupInfo(BaseModel):
"""
Information about a group
"""
class Config:
extra = Extra.forbid
id: Optional[int] = Field(None, description="ID of the group")
groupname: str = Field(..., description="The name of the group")
description: str = Field("", description="Text description of the group")
@validator("groupname", pre=True)
def _valid_groupname(cls, v):
"""Makes sure the groupname is a valid string"""
try:
is_valid_groupname(v)
return v
except Exception as e:
raise ValueError(str(e))
[docs]
class UserInfo(BaseModel):
"""
Information about a user
"""
class Config:
validate_assignment = True
extra = Extra.forbid
# id may be None when used for initial creation
id: Optional[int] = Field(None, allow_mutation=False, description="The id of the user")
auth_type: AuthTypeEnum = Field(
AuthTypeEnum.password, allow_mutation=False, description="Type of authentication the user uses"
)
username: str = Field(..., allow_mutation=False, description="The username of this user")
role: str = Field(..., description="The role this user belongs to")
groups: List[str] = Field([], description="Groups this user belongs to")
enabled: bool = Field(..., description="Whether this user is enabled or not")
fullname: constr(max_length=128) = Field("", description="The full name or description of the user")
organization: constr(max_length=128) = Field("", description="The organization the user belongs to")
email: constr(max_length=128) = Field("", description="The email address for the user")
@validator("username", pre=True)
def _valid_username(cls, v):
"""Makes sure the username is a valid string"""
try:
is_valid_username(v)
return v
except Exception as e:
raise ValueError(str(e))
@validator("role", pre=True)
def _valid_rolename(cls, v):
"""Makes sure the rolename is a valid string"""
try:
is_valid_rolename(v)
return v
except Exception as e:
raise ValueError(str(e))
@validator("groups")
def _valid_groupnames(cls, v):
"""Makes sure the groupnames are valid strings"""
try:
for x in v:
is_valid_groupname(x)
return v
except Exception as e:
raise ValueError(str(e))