-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ldap interface integration in the glauth-k8s
- Loading branch information
1 parent
581d684
commit 493ccbc
Showing
12 changed files
with
285 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,16 @@ | ||
options: | ||
log_level: | ||
description: | | ||
Configures the log level. | ||
Configures the log level. | ||
Acceptable values are: "info", "debug", "warning", "error" and "critical" | ||
default: "info" | ||
type: string | ||
base_dn: | ||
description: base DN | ||
description: The base DN | ||
default: "dc=glauth,dc=com" | ||
type: string | ||
hostname: | ||
description: The hostname of the LDAP server | ||
default: "ldap.canonical.com" | ||
type: string |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
cosl | ||
dacite ~= 1.8.0 | ||
Jinja2 | ||
lightkube | ||
lightkube-models | ||
ops >= 2.2.0 | ||
psycopg[binary] | ||
SQLAlchemy | ||
tenacity ~= 8.2.3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import logging | ||
from typing import Any, Optional, Type | ||
|
||
from sqlalchemy import ( | ||
ColumnExpressionArgument, | ||
Integer, | ||
ScalarResult, | ||
String, | ||
create_engine, | ||
select, | ||
) | ||
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class Base(DeclarativeBase): | ||
pass | ||
|
||
|
||
# https://github.com/glauth/glauth-postgres/blob/main/postgres.go | ||
class User(Base): | ||
__tablename__ = "users" | ||
|
||
id: Mapped[int] = mapped_column(primary_key=True) | ||
name: Mapped[str] = mapped_column(String, name="name", unique=True) | ||
uid_number: Mapped[int] = mapped_column(name="uidnumber") | ||
gid_number: Mapped[int] = mapped_column(name="primarygroup") | ||
password_sha256: Mapped[Optional[str]] = mapped_column(name="passsha256") | ||
password_bcrypt: Mapped[Optional[str]] = mapped_column(name="passbcrypt") | ||
|
||
|
||
class Group(Base): | ||
__tablename__ = "groups" | ||
|
||
id = mapped_column(Integer, primary_key=True) | ||
name: Mapped[str] = mapped_column(name="name", unique=True) | ||
gid_number: Mapped[int] = mapped_column(name="gidnumber") | ||
|
||
|
||
class Capability(Base): | ||
__tablename__ = "capabilities" | ||
|
||
id = mapped_column(Integer, primary_key=True) | ||
user_id: Mapped[int] = mapped_column(name="userid") | ||
action: Mapped[str] = mapped_column(default="search") | ||
object: Mapped[str] = mapped_column(default="*") | ||
|
||
|
||
class Operation: | ||
def __init__(self, dsn: str) -> None: | ||
self._dsn = dsn | ||
|
||
def __enter__(self) -> "Operation": | ||
engine = create_engine(self._dsn) | ||
self._session = Session(engine) | ||
return self | ||
|
||
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: | ||
if exc_type: | ||
logger.error( | ||
f"The database operation failed. The exception " f"{exc_type} raised: {exc_val}" | ||
) | ||
self._session.rollback() | ||
else: | ||
self._session.commit() | ||
|
||
self._session.close() | ||
|
||
def select( | ||
self, table: Type[Base], *criteria: ColumnExpressionArgument | ||
) -> Optional[ScalarResult]: | ||
return self._session.scalars(select(table).filter(*criteria)).first() | ||
|
||
def add(self, entity: Base) -> None: | ||
self._session.add(entity) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import hashlib | ||
import logging | ||
from dataclasses import dataclass | ||
from secrets import token_bytes | ||
from typing import Optional | ||
|
||
from charms.glauth_k8s.v0.ldap import LdapProviderData | ||
from configs import DatabaseConfig | ||
from constants import DEFAULT_GID, DEFAULT_UID, GLAUTH_LDAP_PORT | ||
from database import Capability, Group, Operation, User | ||
from ops.charm import CharmBase | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
@dataclass(frozen=True) | ||
class BindAccount: | ||
cn: Optional[str] = None | ||
ou: Optional[str] = None | ||
password: Optional[str] = None | ||
|
||
|
||
def _create_bind_account(dsn: str, user_name: str, group_name: str) -> BindAccount: | ||
with Operation(dsn) as op: | ||
if not op.select(Group, Group.name == group_name): | ||
group = Group(name=group_name, gid_number=DEFAULT_GID) | ||
op.add(group) | ||
|
||
if not (user := op.select(User, User.name == user_name)): | ||
new_password = hashlib.sha256(token_bytes()).hexdigest() | ||
user = User( | ||
name=user_name, | ||
uid_number=DEFAULT_UID, | ||
gid_number=DEFAULT_GID, | ||
password_sha256=new_password, | ||
) | ||
op.add(user) | ||
password = user.password_bcrypt or user.password_sha256 | ||
|
||
if not op.select(Capability, Capability.user_id == DEFAULT_UID): | ||
capability = Capability(user_id=DEFAULT_UID) | ||
op.add(capability) | ||
|
||
return BindAccount(user_name, group_name, password) | ||
|
||
|
||
class LdapIntegration: | ||
def __init__(self, charm: CharmBase): | ||
self._charm = charm | ||
self._bind_account: Optional[BindAccount] = None | ||
|
||
def load_bind_account(self, user: str, group: str) -> None: | ||
database_config = DatabaseConfig.load(self._charm.database_requirer) | ||
self._bind_account = _create_bind_account(database_config.dsn, user, group) | ||
|
||
@property | ||
def provider_data(self) -> Optional[LdapProviderData]: | ||
if not self._bind_account: | ||
return None | ||
|
||
return LdapProviderData( | ||
url=f"ldap://{self._charm.config.get('hostname')}:{GLAUTH_LDAP_PORT}", | ||
base_dn=self._charm.config.get("base_dn"), | ||
bind_dn=f"cn={self._bind_account.cn},ou={self._bind_account.ou},{self._charm.config.get('base_dn')}", | ||
bind_password_secret=self._bind_account.password or "", | ||
auth_method="simple", | ||
starttls=True, | ||
) |
Oops, something went wrong.