[Solution] Update to ONAP Montreal releases version
- update controller/odlux images
- change persistence from elasticsearch to mariaDB
- improved user handling for identity
- support indentity theme
Issue-ID: OAM-403
Change-Id: I28bec223a656af1dcfe61b47f5df43fba4545a52
Signed-off-by: demskeq8 <alexander.dehn@highstreet-technologies.com>
diff --git a/solution/smo/common/.env b/solution/smo/common/.env
index cc337b4..392b892 100644
--- a/solution/smo/common/.env
+++ b/solution/smo/common/.env
@@ -38,7 +38,7 @@
IDENTITY_PROVIDER_URL=https://identity.${HTTP_DOMAIN}
# PERSISTENCE (including SDN-R Database)
-PERSISTENCE_IMAGE=docker.elastic.co/elasticsearch/elasticsearch-oss:7.9.3
+PERSISTENCE_IMAGE=mariadb:11.1.2
## ZooKeeper
ZOOKEEPER_IMAGE=nexus3.onap.org:10001/onap/dmaap/zookeeper:6.0.3
diff --git a/solution/smo/common/docker-compose.yaml b/solution/smo/common/docker-compose.yaml
index 7afa49b..772d073 100755
--- a/solution/smo/common/docker-compose.yaml
+++ b/solution/smo/common/docker-compose.yaml
@@ -14,27 +14,29 @@
# limitations under the License.
#
# no more versions needed! Compose spec supports all features w/o a version
+version: "3.8"
services:
-
gateway:
image: ${TRAEFIK_IMAGE}
container_name: gateway
hostname: gateway
healthcheck:
test:
- - CMD
- - traefik
- - healthcheck
- - --ping
+ [
+ "CMD",
+ "traefik",
+ "healthcheck",
+ "--ping"
+ ]
interval: 10s
timeout: 5s
retries: 3
restart: always
ports:
- - 80:80
- - 443:443
- - 4334:4334
- - 4335:4335
+ - "80:80"
+ - "443:443"
+ - "4334:4334"
+ - "4335:4335"
command:
- --serverstransport.insecureskipverify=true
- --log.level=${TRAEFIK_LOG_LEVEL}
@@ -72,6 +74,9 @@
traefik.http.middlewares.strip.stripprefix.prefixes: /traefik
traefik.http.routers.gateway.tls: true
traefik.http.services.gateway.loadbalancer.server.port: 8080
+ app: "gateway"
+ deploy: "o-ran-sc-smo-common"
+ solution: "o-ran-sc-smo"
networks:
dmz:
dcn:
@@ -81,44 +86,53 @@
container_name: identitydb
hostname: identitydb
environment:
- - ALLOW_EMPTY_PASSWORD=no
- - POSTGRESQL_USERNAME=keycloak
- - POSTGRESQL_DATABASE=keycloak
- - POSTGRESQL_PASSWORD=keycloak
+ ALLOW_EMPTY_PASSWORD: no
+ POSTGRESQL_USERNAME: keycloak
+ POSTGRESQL_DATABASE: keycloak
+ POSTGRESQL_PASSWORD: keycloak
+ labels:
+ app: "identitydb"
+ deploy: "o-ran-sc-smo-common"
+ solution: "o-ran-sc-smo"
identity:
image: ${IDENTITY_IMAGE}
container_name: identity
hostname: identity
environment:
- - KEYCLOAK_CREATE_ADMIN_USER=true
- - KEYCLOAK_ADMIN_USER=${ADMIN_USERNAME}
- - KEYCLOAK_ADMIN_PASSWORD=${ADMIN_PASSWORD}
- - KEYCLOAK_MANAGEMENT_USER=${IDENTITY_MGMT_USERNAME}
- - KEYCLOAK_MANAGEMENT_PASSWORD=${IDENTITY_MGMT_PASSWORD}
- - KEYCLOAK_DATABASE_HOST=identitydb
- - KEYCLOAK_DATABASE_NAME=keycloak
- - KEYCLOAK_DATABASE_USER=keycloak
- - KEYCLOAK_DATABASE_PASSWORD=keycloak
- - KEYCLOAK_JDBC_PARAMS=sslmode=disable&connectTimeout=30000
- - KEYCLOAK_PRODUCTION=false
- - KEYCLOAK_ENABLE_TLS=true
- - KEYCLOAK_TLS_KEYSTORE_FILE=/opt/bitnami/keycloak/certs/keystore.jks
- - KEYCLOAK_TLS_TRUSTSTORE_FILE=/opt/bitnami/keycloak/certs/truststore.jks
- - KEYCLOAK_TLS_KEYSTORE_PASSWORD=password
- - KEYCLOAK_TLS_TRUSTSTORE_PASSWORD=changeit
+ KEYCLOAK_CREATE_ADMIN_USER: true
+ KEYCLOAK_ADMIN_USER: ${ADMIN_USERNAME}
+ KEYCLOAK_ADMIN_PASSWORD: ${ADMIN_PASSWORD}
+ KEYCLOAK_MANAGEMENT_USER: ${IDENTITY_MGMT_USERNAME}
+ KEYCLOAK_MANAGEMENT_PASSWORD: ${IDENTITY_MGMT_PASSWORD}
+ KEYCLOAK_DATABASE_HOST: identitydb
+ KEYCLOAK_DATABASE_NAME: keycloak
+ KEYCLOAK_DATABASE_USER: keycloak
+ KEYCLOAK_DATABASE_PASSWORD: keycloak
+ KEYCLOAK_JDBC_PARAMS=sslmode: disable&connectTimeout=30000
+ KEYCLOAK_PRODUCTION: false
+ KEYCLOAK_ENABLE_TLS: true
+ KEYCLOAK_TLS_KEYSTORE_FILE: /opt/bitnami/keycloak/certs/keystore.jks
+ KEYCLOAK_TLS_TRUSTSTORE_FILE: /opt/bitnami/keycloak/certs/truststore.jks
+ KEYCLOAK_TLS_KEYSTORE_PASSWORD: password
+ KEYCLOAK_TLS_TRUSTSTORE_PASSWORD: changeit
+ KEYCLOAK_EXTRA_ARGS: "--spi-theme-default=oam"
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime:ro
- ./identity/standalone.xml:/opt/jboss/keycloak/standalone/configuration/standalone.xml
- ./identity/keystore.jks:/opt/bitnami/keycloak/certs/keystore.jks
- ./identity/truststoreONAPall.jks:/opt/bitnami/keycloak/certs/truststore.jks
+ - ./identity/themes/oam:/opt/bitnami/keycloak/themes/oam
labels:
traefik.enable: true
traefik.http.routers.identity.entrypoints: websecure
traefik.http.routers.identity.rule: Host(`identity.${HTTP_DOMAIN}`)
traefik.http.routers.identity.tls: true
traefik.http.services.identity.loadbalancer.server.port: 8080
+ app: "identity"
+ deploy: "o-ran-sc-smo-common"
+ solution: "o-ran-sc-smo"
depends_on:
identitydb:
condition: service_started
@@ -132,7 +146,31 @@
image: ${PERSISTENCE_IMAGE}
container_name: persistence
environment:
- - discovery.type=single-node
+ MARIADB_ROOT_PASSWORD: admin
+ MARIADB_DATABASE: sdnrdb
+ MARIADB_USER: sdnrdb
+ MARIADB_PASSWORD: sdnrdb
+ MARIADB_EXTRA_FLAGS: --bind-address=* --max_connections=400
+ MYSQL_ROOT_PASSWORD: admin
+ MYSQL_DATABASE: sdnrdb
+ MYSQL_USER: sdnrdb
+ MYSQL_PASSWORD: sdnrdb
+ labels:
+ app: "persistence"
+ deploy: "o-ran-sc-smo-common"
+ solution: "o-ran-sc-smo"
+ healthcheck:
+ interval: 30s
+ retries: 3
+ test:
+ [
+ "CMD",
+ "healthcheck.sh",
+ "--su-mysql",
+ "--connect",
+ "--innodb_initialized"
+ ]
+ timeout: 30s
zookeeper:
image: ${ZOOKEEPER_IMAGE}
@@ -150,6 +188,10 @@
ZOOKEEPER_SERVER_ID:
volumes:
- ./zookeeper/zk_server_jaas.conf:/etc/zookeeper/secrets/jaas/zk_server_jaas.conf
+ labels:
+ app: "zookeeper"
+ deploy: "o-ran-sc-smo-common"
+ solution: "o-ran-sc-smo"
kafka:
image: ${KAFKA_IMAGE}
@@ -169,6 +211,10 @@
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
# Reduced the number of partitions only to avoid the timeout error for the first subscribe call in slow environment
KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS: 1
+ labels:
+ app: "kafka"
+ deploy: "o-ran-sc-smo-common"
+ solution: "o-ran-sc-smo"
volumes:
- ./kafka/zk_client_jaas.conf:/etc/kafka/secrets/jaas/zk_client_jaas.conf
depends_on:
@@ -192,6 +238,9 @@
traefik.http.routers.kafka-bridge.rule: Host(`kafka-bridge.${HTTP_DOMAIN}`)
traefik.http.routers.kafka-bridge.tls: true
traefik.http.services.kafka-bridge.loadbalancer.server.port: 8080
+ app: "kafka-bridge"
+ deploy: "o-ran-sc-smo-common"
+ solution: "o-ran-sc-smo"
volumes:
- ./kafka-bridge:/opt/strimzi/config
depends_on:
@@ -222,6 +271,9 @@
traefik.http.routers.topology.rule: Host(`topology.${HTTP_DOMAIN}`)
traefik.http.routers.topology.tls: true
traefik.http.services.topology.loadbalancer.server.port: 8181
+ app: "topology"
+ deploy: "o-ran-sc-smo-common"
+ solution: "o-ran-sc-smo"
networks:
dmz:
default:
@@ -242,6 +294,9 @@
traefik.http.routers.messages.rule: Host(`messages.${HTTP_DOMAIN}`)
traefik.http.routers.messages.tls: true
traefik.http.services.messages.loadbalancer.server.port: 3904
+ app: "messages"
+ deploy: "o-ran-sc-smo-common"
+ solution: "o-ran-sc-smo"
depends_on:
kafka:
condition: service_started
diff --git a/solution/smo/common/identity/authentication.json b/solution/smo/common/identity/authentication.json
index 2f91979..868c790 100644
--- a/solution/smo/common/identity/authentication.json
+++ b/solution/smo/common/identity/authentication.json
@@ -1,111 +1,111 @@
{
- "users": [
- {
- "firstName": "Leia",
- "lastName": "Organa",
- "email": "leia.organa@sdnr.onap.org",
- "enabled": "true",
- "username": "leia.organa",
- "credentials": [
+ "users": [
{
- "type": "password",
- "value": "Default4SDN!",
- "temporary": true
- }
- ],
- "requiredActions": [
- "UPDATE_PASSWORD"
- ]
- },
- {
- "firstName": "R2",
- "lastName": "D2",
- "email": "r2.d2@sdnr.onap.org",
- "enabled": "true",
- "username": "r2.d2",
- "credentials": [
+ "firstName": "Leia",
+ "lastName": "Organa",
+ "email": "leia.organa@sdnr.onap.org",
+ "enabled": "",
+ "username": "leia.organa",
+ "credentials": [
+ {
+ "type": "password",
+ "value": "Default4SDN!",
+ "temporary": "true"
+ }
+ ],
+ "requiredActions": [
+ "UPDATE_PASSWORD"
+ ]
+ },
{
- "type": "password",
- "value": "Default4SDN!",
- "temporary": true
- }
- ],
- "requiredActions": [
- "UPDATE_PASSWORD"
- ]
- },
- {
- "firstName": "Luke",
- "lastName": "Skywalker",
- "email": "luke.skywalker@sdnr.onap.org",
- "enabled": "true",
- "username": "luke.skywalker",
- "credentials": [
+ "firstName": "R2",
+ "lastName": "D2",
+ "email": "r2.d2@sdnr.onap.org",
+ "enabled": "",
+ "username": "r2.d2",
+ "credentials": [
+ {
+ "type": "password",
+ "value": "Default4SDN!",
+ "temporary": "true"
+ }
+ ],
+ "requiredActions": [
+ "UPDATE_PASSWORD"
+ ]
+ },
{
- "type": "password",
- "value": "Default4SDN!",
- "temporary": true
- }
- ],
- "requiredActions": [
- "UPDATE_PASSWORD"
- ]
- },
- {
- "firstName": "Jargo",
- "lastName": "Fett",
- "email": "jargo.fett@sdnr.onap.org",
- "enabled": "true",
- "username": "jargo.fett",
- "credentials": [
+ "firstName": "Luke",
+ "lastName": "Skywalker",
+ "email": "luke.skywalker@sdnr.onap.org",
+ "enabled": "",
+ "username": "luke.skywalker",
+ "credentials": [
+ {
+ "type": "password",
+ "value": "Default4SDN!",
+ "temporary": "true"
+ }
+ ],
+ "requiredActions": [
+ "UPDATE_PASSWORD"
+ ]
+ },
{
- "type": "password",
- "value": "Default4SDN!",
- "temporary": true
- }
- ],
- "requiredActions": [
- "UPDATE_PASSWORD"
- ]
- },
- {
- "firstName": "Martin",
- "lastName": "Skorupski",
- "email": "martin.skorupski@highstreet-technologies.com",
- "enabled": "true",
- "username": "martin.skorupski",
- "credentials": [
+ "firstName": "Jargo",
+ "lastName": "Fett",
+ "email": "jargo.fett@sdnr.onap.org",
+ "enabled": "",
+ "username": "jargo.fett",
+ "credentials": [
+ {
+ "type": "password",
+ "value": "Default4SDN!",
+ "temporary": "true"
+ }
+ ],
+ "requiredActions": [
+ "UPDATE_PASSWORD"
+ ]
+ },
{
- "type": "password",
- "value": "Default4SDN!",
- "temporary": true
+ "firstName": "Martin",
+ "lastName": "Skorupski",
+ "email": "martin.skorupski@highstreet-technologies.com",
+ "enabled": "",
+ "username": "martin.skorupski",
+ "credentials": [
+ {
+ "type": "password",
+ "value": "Default4SDN!",
+ "temporary": "true"
+ }
+ ],
+ "requiredActions": [
+ "UPDATE_PASSWORD"
+ ]
}
- ],
- "requiredActions": [
- "UPDATE_PASSWORD"
- ]
- }
- ],
- "grants": [
- {
- "username": "leia.organa",
- "role": "administration"
- },
- {
- "username": "r2.d2",
- "role": "administration"
- },
- {
- "username": "luke.skywalker",
- "role": "provision"
- },
- {
- "username": "jargo.fett",
- "role": "supervision"
- },
- {
- "username": "martin.skorupski",
- "role": "administration"
- }
- ]
+ ],
+ "grants": [
+ {
+ "username": "leia.organa",
+ "role": "administration"
+ },
+ {
+ "username": "r2.d2",
+ "role": "administration"
+ },
+ {
+ "username": "luke.skywalker",
+ "role": "provision"
+ },
+ {
+ "username": "jargo.fett",
+ "role": "supervision"
+ },
+ {
+ "username": "martin.skorupski",
+ "role": "administration"
+ }
+ ]
}
\ No newline at end of file
diff --git a/solution/smo/common/identity/config.py b/solution/smo/common/identity/config.py
index 5a8bf44..37967e1 100644
--- a/solution/smo/common/identity/config.py
+++ b/solution/smo/common/identity/config.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-#############################################################################
-# Copyright 2023 highstreet technologies GmbH
+################################################################################
+# Copyright 2021 highstreet technologies GmbH
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
@@ -16,26 +16,27 @@
#
# importing the sys, json, requests library
+from sqlite3 import TimeFromTicks
+from jproperties import Properties
import os
-import pathlib
import sys
import json
import time
-import getpass
-import requests
import re
+import requests
+import getpass
import warnings
-from jproperties import Properties
from typing import List
+
warnings.filterwarnings('ignore', message='Unverified HTTPS request')
+
+
# global configurations
-
-
-def get_environment_variable(name):
+def get_env(name):
configs = Properties()
- path = pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
- env_file = str(path.parent.absolute()) + '/.env'
- with open(env_file, "rb") as read_prop:
+ envFile = os.path.dirname(os.path.abspath(__file__)) + '/' + '../' + '.env'
+
+ with open(envFile, "rb") as read_prop:
configs.load(read_prop)
value = configs.get(name).data
@@ -45,54 +46,61 @@
match = next(matches, None)
if match is None:
break
- inner = get_environment_variable(match.group(1))
- value = value.replace("${" + match.group(1) + "}", inner )
+ inner = get_env(match.group(1))
+ value = value.replace("${" + match.group(1) + "}", inner)
return value
-def load_arguments(args: List[str]) -> tuple:
- realm_file = os.path.dirname(os.path.abspath(
- __file__)) + '/o-ran-sc-realm.json'
- auth_file = os.path.dirname(os.path.abspath(
- __file__)) + '/authentication.json'
- ready_timeout = 180
+def loadArgs(args: List[str]) -> tuple:
+ realmFile = os.path.dirname(os.path.abspath(__file__)) + '/o-ran-sc-realm.json'
+ authFile = os.path.dirname(os.path.abspath(__file__)) + '/authentication.json'
+ readyTimeout = 180
args.pop(0)
while len(args) > 0:
arg = args.pop(0)
if arg == '--auth' and len(args) > 0:
- auth_file = args.pop(0)
- print('overwriting auth file: {}'.format(auth_file))
+ authFile = args.pop(0)
+ print('overwriting auth file: {}'.format(authFile))
elif arg == '--realm' and len(args) > 0:
- realm_file = args.pop(0)
- print('overwriting realm file: {}'.format(realm_file))
+ realmFile = args.pop(0)
+ print('overwriting realm file: {}'.format(realmFile))
elif arg == '--timeout' and len(args) > 0:
- ready_timeout = int(args.pop(0))
- print('waiting for ready {} seconds'.format(ready_timeout))
+ readyTimeout = int(args.pop(0))
+ print('waiting for ready {} seconds'.format(readyTimeout))
- return (realm_file, auth_file, ready_timeout)
+ return (realmFile, authFile, readyTimeout)
def isReady(timeoutSeconds=180):
url = getBaseUrl()
- print(f'url={url}')
+ print(url)
+ response = None
+ print("waiting for ready state", end='')
while timeoutSeconds > 0:
try:
response = requests.get(url, verify=False, headers={})
+ print(response)
except:
- response = None
+ pass
if response is not None and response.status_code == 200:
+ print('succeeded')
return True
time.sleep(1)
timeoutSeconds -= 1
+ print('.', end='', flush=True)
return False
def getBaseUrl():
- return get_environment_variable("IDENTITY_PROVIDER_URL")
-
-# Request a token for further communication
+ try:
+ if get_env("USE_LOCAL_HOST_FOR_IDENTITY_CONFIG").strip("'\"") == "true":
+ return get_env("IDENTITY_PROVIDER_URL_LOCAL_HOST")
+ except AttributeError:
+ print("Using IDENTITY_PROVIDER_URL")
+ return get_env("IDENTITY_PROVIDER_URL")
+# Request a token for futher communication
def getToken():
url = base + '/realms/master/protocol/openid-connect/token'
headers = {
@@ -106,8 +114,7 @@
'password': password
}
try:
- response = requests.post(url, verify=False, auth=(
- username, password), data=body, headers=headers)
+ response = requests.post(url, verify=False, auth=(username, password), data=body, headers=headers)
except requests.exceptions.Timeout:
sys.exit('HTTP request failed, please check you internet connection.')
except requests.exceptions.TooManyRedirects:
@@ -122,9 +129,8 @@
else:
sys.exit('Getting token failed.')
+
# create the default realm from file
-
-
def createRealm(token, realm):
url = base + '/admin/realms'
auth = 'bearer ' + token
@@ -134,8 +140,7 @@
'authorization': auth
}
try:
- response = requests.post(
- url, verify=False, json=realm, headers=headers)
+ response = requests.post(url, verify=False, json=realm, headers=headers)
except requests.exceptions.Timeout:
sys.exit('HTTP request failed, please check you internet connection.')
except requests.exceptions.TooManyRedirects:
@@ -146,9 +151,8 @@
return response.status_code >= 200 and response.status_code < 300
+
# Check if default realm exists
-
-
def checkRealmExists(token, realmId):
url = base + '/admin/realms/' + realmId
auth = 'bearer ' + token
@@ -172,9 +176,8 @@
# sys.exit('Getting realm failed.')
return False
+
# create a user in default realm
-
-
def createUser(token, realmConfig, user):
realmId = realmConfig['id']
url = base + '/admin/realms/' + realmId + '/users'
@@ -198,9 +201,8 @@
else:
print('User creation', user['username'], 'failed!\n', response.text)
+
# creates User accounts in realm based a file
-
-
def createUsers(token, realmConfig, authConfig):
for user in authConfig['users']:
createUser(token, realmConfig, user)
@@ -216,24 +218,24 @@
{
"type": "password",
"value": password,
- "temporary": True
+ "temporary": False
}
- ],
- "requiredActions": [
- "UPDATE_PASSWORD"
]
}
createUser(token, realmConfig, systemUser)
+
# Grants a role to a user
-
-
-def addUserRole(user: dict, role: dict, options: dict):
+def addUserRole(user: dict, role: list, options: dict):
url = options['url'] + '/' + user['id'] + '/role-mappings/realm'
try:
- response = requests.post(url, verify=False, json=[
- {'id': role['id'], 'name':role['name']}],
- headers=options['headers'])
+ for irole in role:
+ response = requests.post(url, verify=False, json=[{'id': irole['id'], 'name': irole['name']}],
+ headers=options['headers'])
+ if response.status_code >= 200 and response.status_code < 300:
+ print('User role', user['username'], irole['name'], 'created!')
+ else:
+ print('Creation of user role', user['username'], irole['name'], 'failed!\n', response.text)
except requests.exceptions.Timeout:
sys.exit('HTTP request failed, please check you internet connection.')
except requests.exceptions.TooManyRedirects:
@@ -242,28 +244,24 @@
# catastrophic error. bail.
raise SystemExit(e)
- if response.status_code >= 200 and response.status_code < 300:
- print('User role', user['username'], role['name'], 'created!')
- else:
- print('Creation of user role',
- user['username'], role['name'], 'failed!\n', response.text)
# searches for the role of a given user
-
-
def findRole(username: str, authConfig: dict, realmConfig: dict) -> dict:
+ roleList = []
+ roleNames = []
roleName = 'administration'
for grant in authConfig['grants']:
if grant['username'] == username:
roleName = grant['role']
- for role in realmConfig['roles']['realm']:
- if role['name'] == roleName:
- return role
- return None
+ roleNames = roleName.split(",") # A user can have multiple roles, comma separated
+ for iroleName in roleNames:
+ for role in realmConfig['roles']['realm']:
+ if role['name'] == iroleName:
+ roleList.append(role)
+ return roleList
+
# adds roles to users
-
-
def addUserRoles(token, realmConfig, authConfig):
realmId = realmConfig['id']
url = base + '/admin/realms/' + realmId + '/users'
@@ -296,12 +294,12 @@
else:
sys.exit('Getting users failed.')
+
# main
-
-(realmFile, authFile, readyTimeout) = load_arguments(sys.argv)
-username = get_environment_variable('ADMIN_USERNAME')
-password = get_environment_variable('ADMIN_PASSWORD')
+(realmFile, authFile, readyTimeout) = loadArgs(sys.argv)
+username = get_env('ADMIN_USERNAME')
+password = get_env('ADMIN_PASSWORD')
base = getBaseUrl()
isReady(readyTimeout)
token = getToken()
diff --git a/solution/smo/common/identity/themes/README.md b/solution/smo/common/identity/themes/README.md
new file mode 100644
index 0000000..98d3823
--- /dev/null
+++ b/solution/smo/common/identity/themes/README.md
@@ -0,0 +1,6 @@
+# add themes to solution
+- copy `org.keycloak.keycloak-themes-XX.Y.Z.jar` from image and unzip
+- copy keycloak themes into directory a themes subdirectory directory with
+- modify css and resources (see dev resoures in keycloak)
+- use `- KEYCLOAK_EXTRA_ARGS="--log-level=DEBUG --spi-theme-static-max-age=-1 --spi-theme-cache-themes=false --spi-theme-cache-templates=false"` for online development
+- add `KEYCLOAK_EXTRA_ARGS="--spi-theme-default=5gberlin"` to as environment in docker-compose.yml for identity to select as default theme
\ No newline at end of file
diff --git a/solution/smo/common/identity/themes/oam/README.md b/solution/smo/common/identity/themes/oam/README.md
new file mode 100644
index 0000000..17e8a14
--- /dev/null
+++ b/solution/smo/common/identity/themes/oam/README.md
@@ -0,0 +1,16 @@
+# create custom theme
+
+A detailed description of the theme creation can be found in the [keycloak documentation](https://www.keycloak.org/docs/latest/server_development/#_themes).
+It's not necessary to create a new theme from scratch. You can inherit from a base theme and override only the necessary
+parts.
+
+- use `- KEYCLOAK_EXTRA_ARGS="--log-level=DEBUG --spi-theme-static-max-age=-1 --spi-theme-cache-themes=false --spi-theme-cache-templates=false"` for theme development
+
+# add themes to solution
+
+After creating the theme, you can add it to the solution. The following steps are necessary:
+
+- mount the themes directory into the keycloak container
+ - target directory: `/opt/bitnami/keycloak/themes/[custom-theme-name]`
+- add `KEYCLOAK_EXTRA_ARGS="--spi-theme-default=[custom-theme-name]"` to the environment section in docker-compose.yml
+ for identity to select as default theme
\ No newline at end of file
diff --git a/solution/smo/common/identity/themes/oam/account/theme.properties b/solution/smo/common/identity/themes/oam/account/theme.properties
new file mode 100644
index 0000000..12d9c19
--- /dev/null
+++ b/solution/smo/common/identity/themes/oam/account/theme.properties
@@ -0,0 +1,2 @@
+parent=keycloak
+import=common/keycloak
\ No newline at end of file
diff --git a/solution/smo/common/identity/themes/oam/admin/theme.properties b/solution/smo/common/identity/themes/oam/admin/theme.properties
new file mode 100644
index 0000000..12d9c19
--- /dev/null
+++ b/solution/smo/common/identity/themes/oam/admin/theme.properties
@@ -0,0 +1,2 @@
+parent=keycloak
+import=common/keycloak
\ No newline at end of file
diff --git a/solution/smo/common/identity/themes/oam/email/theme.properties b/solution/smo/common/identity/themes/oam/email/theme.properties
new file mode 100644
index 0000000..12d9c19
--- /dev/null
+++ b/solution/smo/common/identity/themes/oam/email/theme.properties
@@ -0,0 +1,2 @@
+parent=keycloak
+import=common/keycloak
\ No newline at end of file
diff --git a/solution/smo/common/identity/themes/oam/login/resources/css/styles.css b/solution/smo/common/identity/themes/oam/login/resources/css/styles.css
new file mode 100644
index 0000000..8cb33bc
--- /dev/null
+++ b/solution/smo/common/identity/themes/oam/login/resources/css/styles.css
@@ -0,0 +1,19 @@
+.login-pf body {
+ background: DimGrey none;
+}
+.login-pf body {
+ background: url("../img/o-ran-sc-smo-oam-keyclock-background.png") no-repeat center center fixed;
+ background-size: cover;
+ height: 100%;
+}
+div.kc-logo-text {
+ background-image: url(../img/o-ran-sc-logo.png);
+ background-repeat: no-repeat;
+ height: 63px;
+ width: 300px;
+ margin: 0 auto;
+}
+.card-pf {
+ background: #bbb;
+ opacity: 0.8;
+}
\ No newline at end of file
diff --git a/solution/smo/common/identity/themes/oam/login/resources/img/o-ran-sc-logo.png b/solution/smo/common/identity/themes/oam/login/resources/img/o-ran-sc-logo.png
new file mode 100644
index 0000000..c3b6ce5
--- /dev/null
+++ b/solution/smo/common/identity/themes/oam/login/resources/img/o-ran-sc-logo.png
Binary files differ
diff --git a/solution/smo/common/identity/themes/oam/login/resources/img/o-ran-sc-smo-oam-keyclock-background.png b/solution/smo/common/identity/themes/oam/login/resources/img/o-ran-sc-smo-oam-keyclock-background.png
new file mode 100644
index 0000000..c8f24dd
--- /dev/null
+++ b/solution/smo/common/identity/themes/oam/login/resources/img/o-ran-sc-smo-oam-keyclock-background.png
Binary files differ
diff --git a/solution/smo/common/identity/themes/oam/login/theme.properties b/solution/smo/common/identity/themes/oam/login/theme.properties
new file mode 100644
index 0000000..cad2a08
--- /dev/null
+++ b/solution/smo/common/identity/themes/oam/login/theme.properties
@@ -0,0 +1,3 @@
+parent=keycloak
+import=common/keycloak
+styles=css/login.css css/styles.css
\ No newline at end of file
diff --git a/solution/smo/common/identity/themes/oam/welcome/theme.properties b/solution/smo/common/identity/themes/oam/welcome/theme.properties
new file mode 100644
index 0000000..12d9c19
--- /dev/null
+++ b/solution/smo/common/identity/themes/oam/welcome/theme.properties
@@ -0,0 +1,2 @@
+parent=keycloak
+import=common/keycloak
\ No newline at end of file
diff --git a/solution/smo/oam/.env b/solution/smo/oam/.env
index 8541289..588a941 100644
--- a/solution/smo/oam/.env
+++ b/solution/smo/oam/.env
@@ -28,13 +28,14 @@
IDENTITY_PROVIDER_URL=https://identity.${HTTP_DOMAIN}
# SDN Controller
-SDNC_IMAGE=nexus3.onap.org:10001/onap/sdnc-image:2.4.2
+SDNC_IMAGE=nexus3.onap.org:10001/onap/sdnc-web-image:2.6.1
SDNC_REST_PORT=8181
+SDNR_WEBSOCKET_PORT=8182
SDNC_CERT_DIR=/opt/opendaylight/current/certs
SDNC_ENABLE_OAUTH=true
# SDN Controller Web
-SDNC_WEB_IMAGE=nexus3.onap.org:10001/onap/sdnc-web-image:2.4.2
+SDNC_WEB_IMAGE=nexus3.onap.org:10001/onap/sdnc-web-image:2.6.1
SDNC_WEB_PORT=8080
## VES Collector
diff --git a/solution/smo/oam/controller/mountpoint-registrar.properties b/solution/smo/oam/controller/mountpoint-registrar.properties
index 526c07a..7968841 100644
--- a/solution/smo/oam/controller/mountpoint-registrar.properties
+++ b/solution/smo/oam/controller/mountpoint-registrar.properties
@@ -4,35 +4,43 @@
sdnrUser=admin
sdnrPasswd=${ODL_ADMIN_PASSWORD}
-[fault]
-TransportType=HTTPNOAUTH
-host=messages:3904
-topic=unauthenticated.SEC_FAULT_OUTPUT
-contenttype=application/json
-group=myG
-id=C1
-timeout=2000
-limit=1000
-fetchPause=5000
+[strimzi-kafka]
+strimziEnabled=true
+bootstrapServers=kafka:9092
+securityProtocol=PLAINTEXT
+saslMechanism=PLAIN
+saslJaasConfig=PLAIN
-[pnfRegistration]
-TransportType=HTTPNOAUTH
-host=messages:3904
-topic=unauthenticated.VES_PNFREG_OUTPUT
-contenttype=application/json
-group=myG
-id=C1
-timeout=2000
-limit=1000
+
+[fault]
+topic=unauthenticated.SEC_FAULT_OUTPUT
+consumerGroup=myG
+consumerID=C1
+timeout=20000
+limit=10000
fetchPause=5000
[provisioning]
-TransportType=HTTPNOAUTH
-host=messages:3904
topic=unauthenticated.SEC_3GPP_PROVISIONING_OUTPUT
-contenttype=application/json
-group=myG
-id=C1
+consumerGroup=myG
+consumerID=C1
timeout=20000
limit=10000
-fetchPause=5000
\ No newline at end of file
+fetchPause=5000
+
+[pnfRegistration]
+topic=unauthenticated.VES_PNFREG_OUTPUT
+consumerGroup=myG
+consumerID=C1
+timeout=20000
+limit=10000
+fetchPause=5000
+
+[stndDefinedFault]
+topic=unauthenticated.SEC_3GPP_FAULTSUPERVISION_OUTPUT
+consumerGroup=myG
+consumerID=C1
+timeout=20000
+limit=10000
+fetchPause=5000
+
diff --git a/solution/smo/oam/controller/oauth-aaa-app-config.xml b/solution/smo/oam/controller/oauth-aaa-app-config.xml
index c210e37..8acb414 100644
--- a/solution/smo/oam/controller/oauth-aaa-app-config.xml
+++ b/solution/smo/oam/controller/oauth-aaa-app-config.xml
@@ -15,37 +15,34 @@
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- ~ ============LICENSE_END=======================================================
- ~
- -->
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+~ ============LICENSE_END=======================================================
+ ~
+ -->
-<shiro-configuration xmlns="urn:opendaylight:aaa:app:config">
+ <shiro-configuration xmlns="urn:opendaylight:aaa:app:config">
<main>
- <pair-key>tokenAuthRealm</pair-key>
+ <pair-key>tokenAuthRealm</pair-key>
<pair-value>org.onap.ccsdk.features.sdnr.wt.oauthprovider.OAuth2Realm</pair-value>
- </main>
+ </main>
<main>
- <pair-key>securityManager.realms</pair-key>
+ <pair-key>securityManager.realms</pair-key>
<pair-value>$tokenAuthRealm</pair-value>
- </main>
+ </main>
<!-- Used to support OAuth2 use case. -->
<main>
- <pair-key>authcBasic</pair-key>
- <pair-value>org.opendaylight.aaa.shiro.filters.ODLHttpAuthenticationFilter</pair-value>
- </main>
- <main>
<pair-key>anyroles</pair-key>
<pair-value>org.onap.ccsdk.features.sdnr.wt.oauthprovider.filters.AnyRoleHttpAuthenticationFilter</pair-value>
- </main>
+ </main>
<main>
- <pair-key>authcBearer</pair-key>
- <pair-value>org.onap.ccsdk.features.sdnr.wt.oauthprovider.filters.BearerAndBasicHttpAuthenticationFilter</pair-value>
- </main>
+ <pair-key>authcBearer</pair-key>
+ <!-- <pair-value>org.apache.shiro.web.filter.authc.BearerHttpAuthenticationFilter</pair-value>-->
+ <pair-value>org.onap.ccsdk.features.sdnr.wt.oauthprovider.filters.BearerAndBasicHttpAuthenticationFilter</pair-value>
+ </main>
<!-- in order to track AAA challenge attempts -->
<main>
@@ -53,31 +50,34 @@
<pair-value>org.opendaylight.aaa.shiro.filters.AuthenticationListener</pair-value>
</main>
<main>
- <pair-key>securityManager.authenticator.authenticationListeners</pair-key>
- <pair-value>$accountingListener</pair-value>
- </main>
+ <pair-key>securityManager.authenticator.authenticationListeners</pair-key>
+ <pair-value>$accountingListener</pair-value>
+ </main>
<!-- Model based authorization scheme supporting RBAC for REST endpoints -->
<main>
- <pair-key>dynamicAuthorization</pair-key>
- <pair-value>org.opendaylight.aaa.shiro.realm.MDSALDynamicAuthorizationFilter</pair-value>
- </main>
+ <pair-key>dynamicAuthorization</pair-key>
+ <pair-value>org.opendaylight.aaa.shiro.realm.MDSALDynamicAuthorizationFilter</pair-value>
+ </main>
<urls>
<pair-key>/**/operations/cluster-admin**</pair-key>
- <pair-value>authcBearer, roles[admin]</pair-value>
+ <pair-value>authcBasic, roles[admin]</pair-value>
</urls>
<urls>
<pair-key>/**/v1/**</pair-key>
- <pair-value>authcBearer, roles[admin]</pair-value>
+ <pair-value>authcBasic, roles[admin]</pair-value>
</urls>
- <!-- allow admin only access to write mdsal auth config -->
<urls>
- <pair-key>/rests/**/aaa*/**</pair-key>
- <pair-value>authcBearer, roles[admin]</pair-value>
+ <pair-key>/rests/**/aaa*/**</pair-key>
+ <pair-value>authcBasic, roles[admin]</pair-value>
+ </urls>
+
+ <urls>
+ <pair-key>/**/config/aaa*/**</pair-key>
+ <pair-value>authcBasic, roles[admin]</pair-value>
</urls>
- <!-- anon access for login api -->
<urls>
<pair-key>/oauth/**</pair-key>
<pair-value>anon</pair-value>
@@ -86,23 +86,29 @@
<pair-key>/ready</pair-key>
<pair-value>anon</pair-value>
</urls>
- <!-- anon access for odlux ui -->
<urls>
- <pair-key>/odlux/**</pair-key>
- <pair-value>anon</pair-value>
- </urls>
- <!-- admin only access for apidocs -->
+ <pair-key>/apidoc/**</pair-key>
+ <pair-value>authcBasic, roles[admin]</pair-value>
+ </urls>
+ <!-- these two rules are needed for installCerts.py -->
<urls>
- <pair-key>/apidoc/**</pair-key>
+ <pair-key>/rests/data/network-topology:network-topology</pair-key>
<pair-value>authcBasic, roles[admin]</pair-value>
</urls>
<urls>
- <pair-key>/rests/**</pair-key>
- <pair-value>authcBearer, anyroles["admin,provision"]</pair-value>
- </urls>
- <!-- any other access with configured dynamic filter -->
+ <pair-key>/rests/operations/netconf-keystore*</pair-key>
+ <pair-value>authcBasic, roles[admin]</pair-value>
+ </urls>
+
+ <!-- rfc8040 restconf access with configured dynamic filter -->
<urls>
- <pair-key>/**</pair-key>
- <pair-value>authcBearer, anyroles["admin,provision"]</pair-value>
- </urls>
+ <pair-key>/rests/**</pair-key>
+ <pair-value>authcBearer, dynamicAuthorization</pair-value>
+ </urls>
+ <!-- any other access with configured dynamic filter -->
+<urls>
+<pair-key>/**</pair-key>
+<pair-value>authcBearer, roles[admin]</pair-value>
+</urls>
</shiro-configuration>
+
diff --git a/solution/smo/oam/docker-compose.yaml b/solution/smo/oam/docker-compose.yaml
index 0042d22..bc827ae 100755
--- a/solution/smo/oam/docker-compose.yaml
+++ b/solution/smo/oam/docker-compose.yaml
@@ -15,8 +15,8 @@
#
# no more versions needed! Compose spec supports all features w/o a version
+version: "3.8"
services:
-
odlux:
image: ${SDNC_WEB_IMAGE}
container_name: odlux
@@ -28,12 +28,16 @@
SDNRPROTOCOL: http
SDNRHOST: controller
SDNRPORT: ${SDNC_REST_PORT}
+ SDNRWEBSOCKETPORT: ${SDNR_WEBSOCKET_PORT}
labels:
traefik.enable: true
traefik.http.routers.sdnc-web.entrypoints: websecure
traefik.http.routers.sdnc-web.rule: Host(`odlux.oam.${HTTP_DOMAIN}`)
traefik.http.routers.sdnc-web.tls: true
traefik.http.services.sdnc-web.loadbalancer.server.port: ${SDNC_WEB_PORT}
+ app: "odlux"
+ deploy: "o-ran-sc-smo-oam"
+ solution: "o-ran-sc-smo"
depends_on:
controller:
condition: service_healthy
@@ -56,6 +60,7 @@
environment:
ENABLE_ODL_CLUSTER: false
ENABLE_OAUTH: ${SDNC_ENABLE_OAUTH}
+ ENABLE_ODLUX_RBAC: false
ODL_CERT_DIR: ${SDNC_CERT_DIR}
ODL_ADMIN_PASSWORD: ${ADMIN_PASSWORD}
SDNC_CONFIG_DIR: /opt/onap/ccsdk/data/properties
@@ -66,10 +71,14 @@
SDNRONLY: true
SDNRINIT: true
SDNRDM: true
- SDNRDBURL: http://persistence:9200
+ SDNRDBTYPE: MARIADB
+ SDNRDBURL: jdbc:mysql://persistence:3306/sdnrdb
+ SDNRDBUSERNAME: sdnrdb
+ SDNRDBPASSWORD: sdnrdb
SDNR_NETCONF_CALLHOME_ENABLED: true
A1_ADAPTER_NORTHBOUND: false
JAVA_OPTS: -Xms256m -Xmx4g
+ SDNR_WEBSOCKET_PORT: ${SDNR_WEBSOCKET_PORT}
IDENTITY_PROVIDER_URL: ${IDENTITY_PROVIDER_URL}
SDNC_WEB_URL: https://odlux.oam.${HTTP_DOMAIN}
SDNR_VES_COLLECTOR_ENABLED: true
@@ -107,6 +116,9 @@
traefik.tcp.routers.controller-tls.tls: false
traefik.tcp.routers.controller-tls.service: controller-tls
traefik.tcp.services.controller-tls.loadbalancer.server.port: 4335
+ app: "controller"
+ deploy: "o-ran-sc-smo-oam"
+ solution: "o-ran-sc-smo"
networks:
smo:
dcn:
@@ -117,6 +129,7 @@
context: ./ves-collector
args:
- BASEIMAGE=${VES_COLLECTOR_IMAGE}
+ network: host
container_name: ves-collector
hostname: ves-collector
extra_hosts:
@@ -138,6 +151,9 @@
traefik.http.routers.ves.rule: Host(`ves-collector.dcn.${HTTP_DOMAIN}`)
traefik.http.routers.ves.tls: true
traefik.http.services.ves.loadbalancer.server.port: ${VES_ENDPOINT_PORT}
+ app: "ves-collector"
+ deploy: "o-ran-sc-smo-oam"
+ solution: "o-ran-sc-smo"
networks:
smo:
dcn: