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

feat(docker-jans-scim): allow creating initial persistence entry #2035

Merged
merged 1 commit into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 0 additions & 22 deletions docker-jans-configurator/scripts/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -735,14 +735,6 @@ def radius_ctx(self):
partial(encode_template, fn, self.ctx, basedir),
)

def scim_ctx(self):
self.set_config("scim_client_id", lambda: f"1201.{uuid4()}")
scim_client_pw = self.set_secret("scim_client_pw", get_random_chars)
self.set_secret(
"scim_client_encoded_pw",
partial(encode_text, scim_client_pw, self.get_secret("encoded_salt"))
)

def couchbase_ctx(self):
# TODO: move this to persistence-loader?
self.set_config("couchbaseTrustStoreFn", "/etc/certs/couchbase.pkcs12")
Expand All @@ -758,14 +750,6 @@ def jackrabbit_ctx(self):
def sql_ctx(self):
self.set_secret("sql_password", self.params["sql_pw"])

def casa_ctx(self):
self.set_config("casa_client_id", lambda: f"1902.{uuid4()}")
casa_client_pw = self.set_secret("casa_client_pw", get_random_chars)
self.set_secret(
"casa_client_encoded_pw",
partial(encode_text, casa_client_pw, self.get_secret("encoded_salt"))
)

def generate(self):
opt_scopes = self.params["optional_scopes"]

Expand All @@ -786,9 +770,6 @@ def generate(self):
# self.oxshibboleth_ctx()
# self.radius_ctx()

if "scim" in opt_scopes:
self.scim_ctx()

if "couchbase" in opt_scopes:
self.couchbase_ctx()

Expand All @@ -797,9 +778,6 @@ def generate(self):
if "sql" in opt_scopes:
self.sql_ctx()

if "casa" in opt_scopes:
self.casa_ctx()

self.admin_ui_ctx()
self.jans_cli_ctx()

Expand Down
2 changes: 1 addition & 1 deletion docker-jans-configurator/scripts/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

OPTIONAL_SCOPES = (
"ldap",
"scim",
"couchbase",
"redis",
"sql",
Expand All @@ -34,6 +33,7 @@
"fido2",
"client-api",
"casa",
"scim",
)


Expand Down
24 changes: 0 additions & 24 deletions docker-jans-persistence-loader/scripts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,6 @@ def get_base_ctx(manager):
"auth_openidScopeBackwardCompatibility": manager.config.get("auth_openidScopeBackwardCompatibility"),

"admin_inum": manager.config.get("admin_inum"),
"scim_client_id": manager.config.get("scim_client_id"),
"scim_client_encoded_pw": manager.secret.get("scim_client_encoded_pw"),
"jca_client_id": manager.config.get("jca_client_id"),
"jca_client_encoded_pw": manager.secret.get("jca_client_encoded_pw"),
}
Expand Down Expand Up @@ -201,20 +199,6 @@ def merge_auth_ctx(ctx):
return ctx


def merge_scim_ctx(ctx):
basedir = '/app/templates/jans-scim'
file_mappings = {
'scim_dynamic_conf_base64': 'dynamic-conf.json',
'scim_static_conf_base64': 'static-conf.json',
}

for key, file_ in file_mappings.items():
file_path = os.path.join(basedir, file_)
with open(file_path) as fp:
ctx[key] = generate_base64_contents(fp.read() % ctx)
return ctx


def merge_config_api_ctx(ctx):
def transform_url(url):
auth_server_url = os.environ.get("CN_AUTH_SERVER_URL", "")
Expand Down Expand Up @@ -303,7 +287,6 @@ def prepare_template_ctx(manager):
ctx = merge_extension_ctx(ctx)
ctx = merge_auth_ctx(ctx)
ctx = merge_config_api_ctx(ctx)
ctx = merge_scim_ctx(ctx)
ctx = merge_jans_cli_ctx(manager, ctx)
return ctx

Expand Down Expand Up @@ -346,13 +329,6 @@ def default_files():
"jans-cli/client.ldif",
]

if "scim" in optional_scopes:
files += [
"jans-scim/configuration.ldif",
"jans-scim/scopes.ldif",
"jans-scim/clients.ldif",
]

return files

def user_files():
Expand Down
1 change: 1 addition & 0 deletions docker-jans-scim/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
!scripts
!LICENSE
!requirements.txt
!templates
38 changes: 37 additions & 1 deletion docker-jans-scim/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ RUN wget -q https://maven.jans.io/maven/io/jans/jython-installer/${JYTHON_VERSIO
# ====

ENV CN_VERSION=1.0.2-SNAPSHOT
ENV CN_BUILD_DATE='2022-08-03 12:38'
ENV CN_BUILD_DATE='2022-08-04 15:32'
ENV CN_SOURCE_URL=https://jenkins.jans.io/maven/io/jans/jans-scim-server/${CN_VERSION}/jans-scim-server-${CN_VERSION}.war

# Install SCIM
Expand All @@ -60,6 +60,41 @@ RUN mkdir -p ${JETTY_BASE}/jans-scim/webapps \
&& java -jar ${JETTY_HOME}/start.jar jetty.home=${JETTY_HOME} jetty.base=${JETTY_BASE}/jans-scim --add-to-start=server,deploy,resources,http,http-forwarded,jsp,websocket,cdi-decorate \
&& rm -rf /tmp/jans-scim.war /tmp/WEB-INF

# =====================
# jans-linux-setup sync
# =====================

ENV JANS_LINUX_SETUP_VERSION=2c1414dfc4edd2c14061d375622d56ed8542e08d
ARG JANS_SETUP_DIR=jans-linux-setup/jans_setup

# note that as we're pulling from a monorepo (with multiple project in it)
# we are using partial-clone and sparse-checkout to get the jans-linux-setup code
RUN git clone --filter blob:none --no-checkout https://github.com/janssenproject/jans /tmp/jans \
&& cd /tmp/jans \
&& git sparse-checkout init --cone \
&& git checkout ${JANS_LINUX_SETUP_VERSION} \
&& git sparse-checkout set ${JANS_SETUP_DIR}

RUN mkdir -p /app/static/rdbm \
/app/schema \
/app/templates/jans-scim

# sync static files from linux-setup
RUN cd /tmp/jans \
&& cp ${JANS_SETUP_DIR}/static/rdbm/sql_data_types.json /app/static/rdbm/ \
&& cp ${JANS_SETUP_DIR}/static/rdbm/ldap_sql_data_type_mapping.json /app/static/rdbm/ \
&& cp ${JANS_SETUP_DIR}/static/rdbm/opendj_attributes_syntax.json /app/static/rdbm/ \
&& cp ${JANS_SETUP_DIR}/static/rdbm/sub_tables.json /app/static/rdbm/ \
&& cp ${JANS_SETUP_DIR}/schema/jans_schema.json /app/schema/ \
&& cp ${JANS_SETUP_DIR}/schema/custom_schema.json /app/schema/ \
&& cp ${JANS_SETUP_DIR}/schema/opendj_types.json /app/schema/ \
&& cp ${JANS_SETUP_DIR}/templates/jans-scim/configuration.ldif /app/templates/jans-scim/ \
&& cp ${JANS_SETUP_DIR}/templates/jans-scim/dynamic-conf.json /app/templates/jans-scim/ \
&& cp ${JANS_SETUP_DIR}/templates/jans-scim/static-conf.json /app/templates/jans-scim/

# cleanup
RUN rm -rf /tmp/jans

# ======
# Python
# ======
Expand Down Expand Up @@ -187,6 +222,7 @@ RUN mkdir -p /etc/certs \
COPY jetty/jans-scim.xml ${JETTY_BASE}/jans-scim/webapps/
COPY jetty/log4j2.xml ${JETTY_BASE}/jans-scim/resources/
COPY conf/*.tmpl /app/templates/
COPY templates /app/templates

COPY scripts /app/scripts
RUN chmod +x /app/scripts/entrypoint.sh
Expand Down
91 changes: 91 additions & 0 deletions docker-jans-scim/scripts/bootstrap.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from __future__ import annotations

import json
import os
import re
import typing as _t
from functools import cached_property
from string import Template
from uuid import uuid4

from jans.pycloudlib import get_manager
from jans.pycloudlib.persistence import render_couchbase_properties
Expand All @@ -13,12 +18,25 @@
from jans.pycloudlib.persistence import sync_ldap_truststore
from jans.pycloudlib.persistence import render_sql_properties
from jans.pycloudlib.persistence import render_spanner_properties
from jans.pycloudlib.persistence.couchbase import CouchbaseClient
from jans.pycloudlib.persistence.ldap import LdapClient
from jans.pycloudlib.persistence.spanner import SpannerClient
from jans.pycloudlib.persistence.sql import SqlClient
from jans.pycloudlib.persistence.utils import PersistenceMapper
from jans.pycloudlib.utils import cert_to_truststore
from jans.pycloudlib.utils import encode_text
from jans.pycloudlib.utils import generate_base64_contents
from jans.pycloudlib.utils import get_random_chars

import logging.config
from settings import LOGGING_CONFIG

if _t.TYPE_CHECKING: # pragma: no cover
# imported objects for function type hint, completion, etc.
# these won't be executed in runtime
from jans.pycloudlib.manager import Manager


logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("entrypoint")

Expand Down Expand Up @@ -115,6 +133,9 @@ def main():
modify_webdefault_xml()
configure_logging()

persistence_setup = PersistenceSetup(manager)
persistence_setup.import_ldif_files()


def configure_logging():
# default config
Expand Down Expand Up @@ -185,5 +206,75 @@ def configure_logging():
f.write(tmpl.safe_substitute(config))


class PersistenceSetup:
def __init__(self, manager: Manager) -> None:
self.manager = manager

client_classes = {
"ldap": LdapClient,
"couchbase": CouchbaseClient,
"spanner": SpannerClient,
"sql": SqlClient,
}

# determine persistence type
mapper = PersistenceMapper()
self.persistence_type = mapper.mapping["default"]

# determine persistence client
client_cls = client_classes.get(self.persistence_type)
self.client = client_cls(manager)

@cached_property
def ctx(self) -> dict[str, _t.Any]:
ctx = {
"hostname": self.manager.config.get("hostname"),
}

# SCIM client
ctx["scim_client_id"] = self.manager.config.get("scim_client_id")

if not ctx["scim_client_id"]:
ctx["scim_client_id"] = f"1201.{uuid4()}"
self.manager.config.set("scim_client_id", ctx["scim_client_id"])

ctx["scim_client_pw"] = self.manager.secret.get("scim_client_pw")

if not ctx["scim_client_pw"]:
ctx["scim_client_pw"] = get_random_chars()
self.manager.secret.set("scim_client_pw", ctx["scim_client_pw"])

ctx["scim_client_encoded_pw"] = self.manager.secret.get("scim_client_encoded_pw")

if not ctx["scim_client_encoded_pw"]:
ctx["scim_client_encoded_pw"] = encode_text(
ctx["scim_client_pw"], self.manager.secret.get("encoded_salt"),
).decode()
self.manager.secret.set("scim_client_encoded_pw", ctx["scim_client_encoded_pw"])

# pre-populate scim_dynamic_conf_base64
with open("/app/templates/jans-scim/dynamic-conf.json") as f:
ctx["scim_dynamic_conf_base64"] = generate_base64_contents(f.read() % ctx)

# pre-populate scim_static_conf_base64
with open("/app/templates/jans-scim/static-conf.json") as f:
ctx["scim_static_conf_base64"] = generate_base64_contents(f.read())

return ctx

@cached_property
def ldif_files(self) -> list[str]:
files = [
f"/app/templates/jans-scim/{file_}"
for file_ in ["configuration.ldif", "scopes.ldif", "clients.ldif"]
]
return files

def import_ldif_files(self) -> None:
for file_ in self.ldif_files:
logger.info(f"Importing {file_}")
self.client.create_from_ldif(file_, self.ctx)


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,3 @@ jansSubjectTyp: pairwise
jansTknEndpointAuthMethod: client_secret_basic
objectClass: top
objectClass: jansClnt