],
"requiredActions": [
"UPDATE_PASSWORD"
- ]
+ ],
+ "clientRoles" : {
+ "grafana-ui.app" : [ "grafanaadmin" ]
+ }
},
{
"firstName": "Luke",
],
"requiredActions": [
"UPDATE_PASSWORD"
- ]
+ ],
+ "clientRoles" : {
+ "grafana-ui.app" : [ "editor" ]
+ }
},
{
"firstName": "Jargo",
],
"requiredActions": [
"UPDATE_PASSWORD"
- ]
+ ],
+ "clientRoles" : {
+ "grafana-ui.app" : [ "viewer" ]
+ }
},
{
"firstName": "Martin",
],
"requiredActions": [
"UPDATE_PASSWORD"
- ]
+ ],
+ "clientRoles" : {
+ "grafana-ui.app" : [ "grafanaadmin" ]
+ }
}
],
"grants": [
if response.status_code >= 200 and response.status_code < 300:
print('User', user['username'], 'created!')
+ for client, roles in user.get("clientRoles", {}).items():
+ #print(f"Adding Client Role(s): {roles} from Client: {client} to User: {user['username']}")
+ assignClientRoleToUser(token, realmId, user['username'], client, roles)
else:
print('User creation', user['username'], 'failed!\n', response.text)
for user in authConfig['users']:
createUser(token, realmConfig, user)
- # create a user based on system user
+ # create a user based on unix system user
systemUser = {
"firstName": getpass.getuser(),
"lastName": "",
# catastrophic error. bail.
raise SystemExit(e)
+# Assigns a Client Role to User
+def assignClientRoleToUser(token, realmId, userName, clientName, roles):
+ url = base + '/admin/realms/' + realmId + '/users'
+ auth = 'bearer ' + token
+ headers = {
+ 'content-type': 'application/json',
+ 'accept': 'application/json',
+ 'authorization': auth
+ }
+ # 1. Query userId by userName
+ userId = getUserIdbyName(token, realmId, userName)
+ # 2. Query clientId by clientName
+ clientId = getClientIdByName(token, realmId, clientName)
+ # 3. Iterate through roles and Query roleId for each role as follows - https://identity.smo.o-ran-sc.org/admin/realms/onap/clients/5543ba76-c3bc-42e8-ae79-63a82d6dc2ee/roles?search=grafanaadmin
+ for roleName in roles:
+ roleId = getRoleIdByName(token, realmId, clientId, roleName)
+ # 4. Form a JSON body as - {"id": <roleId>, "name": <roleName>}
+ data = [
+ {
+ "id": roleId,
+ "name": roleName
+ }
+ ]
+ # 5. Form URL as - <url>/<userId>/role-mappings/clients/<clientId>
+ roleAssignUrl = url + '/' + userId + '/role-mappings/clients/' + clientId
+ # 6. Send POST request to assign role to user
+ try:
+ response = requests.post(roleAssignUrl, verify=False, json=data, headers=headers)
+ if response.status_code >= 200 and response.status_code < 300:
+ print('Successfully Assigned role', roleName, 'to user', userName)
+ else:
+ print('Failed to Assign role', roleName, 'to user', userName, response.text)
+ except requests.exceptions.Timeout:
+ sys.exit('HTTP request failed, please check you internet connection.')
+ except requests.exceptions.TooManyRedirects:
+ sys.exit('HTTP request failed, please check your proxy settings.')
+ except requests.exceptions.RequestException as e:
+ # catastrophic error. bail.
+ raise SystemExit(e)
+
+def getRoleIdByName(token, realmId, clientId, roleName) -> str:
+ url = base + '/admin/realms/' + realmId + '/clients/' +clientId + '/roles?search=' + roleName
+ auth = 'bearer ' + token
+ headers = {
+ 'content-type': 'application/json',
+ 'accept': 'application/json',
+ 'authorization': auth
+ }
+ try:
+ response = requests.get(url, verify=False, headers=headers)
+ except requests.exceptions.Timeout:
+ sys.exit('HTTP request failed, please check you internet connection.')
+ except requests.exceptions.TooManyRedirects:
+ sys.exit('HTTP request failed, please check your proxy settings.')
+ except requests.exceptions.RequestException as e:
+ # catastrophic error. bail.
+ raise SystemExit(e)
+
+ if response.status_code >= 200 and response.status_code < 300:
+ role = response.json()
+ roleId = role[0]["id"]
+ return roleId
+
+def getClientIdByName(token, realmId, clientName) -> str:
+ url = base + '/admin/realms/' + realmId + '/clients?clientId=' + clientName
+ auth = 'bearer ' + token
+ headers = {
+ 'content-type': 'application/json',
+ 'accept': 'application/json',
+ 'authorization': auth
+ }
+ try:
+ response = requests.get(url, verify=False, headers=headers)
+ except requests.exceptions.Timeout:
+ sys.exit('HTTP request failed, please check you internet connection.')
+ except requests.exceptions.TooManyRedirects:
+ sys.exit('HTTP request failed, please check your proxy settings.')
+ except requests.exceptions.RequestException as e:
+ # catastrophic error. bail.
+ raise SystemExit(e)
+
+ if response.status_code >= 200 and response.status_code < 300:
+ client = response.json()
+ clientId = client[0]["id"]
+ return clientId
+
+def getUserIdbyName(token, realmId, userName) -> str:
+ url = base + '/admin/realms/' + realmId + '/users?username=' + userName
+ auth = 'bearer ' + token
+ headers = {
+ 'content-type': 'application/json',
+ 'accept': 'application/json',
+ 'authorization': auth
+ }
+ try:
+ response = requests.get(url, verify=False, headers=headers)
+ except requests.exceptions.Timeout:
+ sys.exit('HTTP request failed, please check you internet connection.')
+ except requests.exceptions.TooManyRedirects:
+ sys.exit('HTTP request failed, please check your proxy settings.')
+ except requests.exceptions.RequestException as e:
+ # catastrophic error. bail.
+ raise SystemExit(e)
+
+ if response.status_code >= 200 and response.status_code < 300:
+ user = response.json()
+ userId = user[0]["id"]
+ return userId
# searches for the role of a given user
def findRole(username: str, authConfig: dict, realmConfig: dict) -> dict:
],
"odlux.app": [],
"kafka-ui.app": [],
+ "grafana-ui.app": [
+ {
+ "id" : "b072ad1a-818e-4ff9-b98c-3179bd7f4228",
+ "name" : "editor",
+ "description" : "Grafana Read Write Role",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "9fc6cecf-f3a8-48a8-8065-b2fc80b8b2f5",
+ "attributes" : { }
+ }, {
+ "id" : "09436bef-901c-44a5-b38d-508273d730ba",
+ "name" : "viewer",
+ "description" : "Read only access Role",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "9fc6cecf-f3a8-48a8-8065-b2fc80b8b2f5",
+ "attributes" : { }
+ }, {
+ "id" : "37e3d5fc-41d6-4926-a9c9-e3d96f7f4d6a",
+ "name" : "grafanaadmin",
+ "description" : "Grafana Administrator Role",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "9fc6cecf-f3a8-48a8-8065-b2fc80b8b2f5",
+ "attributes" : { }
+ }
+ ],
"security-admin-console": [],
"admin-cli": [],
"account-console": [],
"manage": true
}
},
+ {
+ "clientId": "grafana-ui.app",
+ "name": "Grafana UI",
+ "description": "",
+ "rootUrl": "https://grafana.smo.o-ran-sc.org",
+ "adminUrl": "https://grafana.smo.o-ran-sc.org",
+ "baseUrl": "",
+ "surrogateAuthRequired": false,
+ "enabled": true,
+ "alwaysDisplayInConsole": false,
+ "clientAuthenticatorType": "client-secret",
+ "secret": "lVPuFWZlOV7yAbV1FIuaM0FOodD7cLTm",
+ "redirectUris": [
+ "https://grafana.smo.o-ran-sc.org/login/generic_oauth"
+ ],
+ "webOrigins": [
+ "https://grafana.smo.o-ran-sc.org"
+ ],
+ "notBefore": 0,
+ "bearerOnly": false,
+ "consentRequired": false,
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": true,
+ "serviceAccountsEnabled": false,
+ "publicClient": false,
+ "frontchannelLogout": true,
+ "protocol": "openid-connect",
+ "attributes": {
+ "realm_client": "false",
+ "oidc.ciba.grant.enabled": "false",
+ "client.secret.creation.time": "1745992471",
+ "backchannel.logout.session.required": "true",
+ "frontchannel.logout.session.required": "true",
+ "oauth2.device.authorization.grant.enabled": "false",
+ "display.on.consent.screen": "false",
+ "backchannel.logout.revoke.offline.tokens": "false"
+ },
+ "authenticationFlowBindingOverrides": {},
+ "fullScopeAllowed": true,
+ "nodeReRegistrationTimeout": -1,
+ "defaultClientScopes": [
+ "web-origins",
+ "acr",
+ "roles",
+ "profile",
+ "basic",
+ "email"
+ ],
+ "optionalClientScopes": [
+ "address",
+ "phone",
+ "offline_access",
+ "microprofile-jwt"
+ ],
+ "access": {
+ "view": true,
+ "configure": true,
+ "manage": true
+ }
+ },
{
"id": "048a9bfc-077a-42a2-afe8-1ec13d3a43a3",
"clientId": "realm-management",
"protocolMapper": "oidc-usermodel-client-role-mapper",
"consentRequired": false,
"config": {
+ "introspection.token.claim" : "true",
+ "userinfo.token.claim" : "true",
+ "id.token.claim" : "true",
"user.attribute": "foo",
"access.token.claim": "true",
"claim.name": "resource_access.${client_id}.roles",
"protocolMapper": "oidc-usermodel-realm-role-mapper",
"consentRequired": false,
"config": {
+ "introspection.token.claim" : "true",
+ "userinfo.token.claim" : "true",
+ "id.token.claim" : "true",
+ "lightweight.claim" : "true",
"user.attribute": "foo",
"access.token.claim": "true",
"claim.name": "realm_access.roles",
-eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJQQTBmYmN1RUNvU1BkZlFCVjgxaE5kVHJwY0wtaE9LS0pGVkFDdEc5bUFrIn0.eyJleHAiOjE3NDI5NjYwODksImlhdCI6MTc0Mjk2NTc4OSwianRpIjoiMWU1OWVmY2EtYWYwZS00Zjc0LTgwMTktNGQ4NzE3YjBhYTZhIiwiaXNzIjoiaHR0cDovL2lkZW50aXR5OjgwODAvcmVhbG1zL25vbnJ0cmljLXJlYWxtIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjQ4NTQyYmQzLWUxNmEtNGYwYS05NzY4LWM4NDVhM2Y2ZThjYiIsInR5cCI6IkJlYXJlciIsImF6cCI6InBtLXByb2R1Y2VyLWpzb24ya2Fma2EiLCJhY3IiOiIxIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtbm9ucnRyaWMtcmVhbG0iLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwiY2xpZW50SG9zdCI6IjE3Mi4xOC4wLjE5IiwiY2xpZW50SWQiOiJwbS1wcm9kdWNlci1qc29uMmthZmthIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtcG0tcHJvZHVjZXItanNvbjJrYWZrYSIsImNsaWVudEFkZHJlc3MiOiIxNzIuMTguMC4xOSJ9.Y5ynDzNr9PRhsXNRDrWyi6HiIvWiA0vS-zP53Iq0NORSqNJTTdqC8oH1yuxlJYFqbs888uQ46CfyF_w7SocuvhvOHY_fouB8H4d9ENE3VAhK8SzfFFKUlRMLrReY9cgzjy64oT3jcJIfdbUpJzRn08eaozfq2WD-9pRqJHxN_9HhZgqKwaCoJS0u22WHEom-pmp0UNRN2o_W9xjEnNcvbX79-DLTWaHL5Bn98Vih4BmKPmDHYf43nmpXuMYPe2O8pwmaYpfAAPpjGXFs_hDGm_B-dPOt-RjHm_zSnh4I4DtKMuPKm6rFbs6hD-boFQUT2SlAfd1XSMdxdQxWzUWiIw
\ No newline at end of file
+eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJWeDlCMUJNbjBLQlZtdHJXQ0xCTVRFWXloNFhiaDNOT2NYbXpudVlZUDZBIn0.eyJleHAiOjE3NDY0NDc4NDUsImlhdCI6MTc0NjQ0NzU0NSwianRpIjoiYTViNzg4ZWUtM2RkNy00NmM3LTk4ZTktMzIxZTc5NmFjNTEyIiwiaXNzIjoiaHR0cHM6Ly9pZGVudGl0eS5zbW8uby1yYW4tc2Mub3JnL3JlYWxtcy9ub25ydHJpYy1yZWFsbSIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiIwNTUyMzJjYi1lMmE1LTQ2M2QtYTQxMi03ZGYwYjQ0ODhlOTYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJwbS1wcm9kdWNlci1qc29uMmthZmthIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWZhdWx0LXJvbGVzLW5vbnJ0cmljLXJlYWxtIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImNsaWVudEhvc3QiOiIxNzIuMjAuMC4xOSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoic2VydmljZS1hY2NvdW50LXBtLXByb2R1Y2VyLWpzb24ya2Fma2EiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjIwLjAuMTkiLCJjbGllbnRfaWQiOiJwbS1wcm9kdWNlci1qc29uMmthZmthIn0.kC4WihXFgbs-tHNK4FmxgbCoXPnJfCvoxhg-gTObfPYWCLnakdJCn-arC-RjOYHNulJGr5broTCvh336whVc4fK6kJMNR2mZ1LcRCm8Dt9qr_bYcJCp2PFqVjzybsOcP4rxjZFGIL7BNvSmc55ST4iXYSwtGeejuQ9iarAxPnU4dWSUJsEX4cO9XgL9P9QriQuZbqFa1YP_bDUahz_Y7mwncq4k_cp2_-weZmRGIRkxF3HiQDD-sOXt7CO8_mMXTJvASQnMipjl21H8f_6etMEgIRz5m1kfsjFvyWI3dogzS3L6BVaOFOO_iqTRIvB7IhsLoIOi2rylVgRbdSh8ztw
\ No newline at end of file
--- /dev/null
+services:
+ grafana:
+ image: grafana/grafana:10.0.0
+ container_name: grafana
+ ports:
+ - "3000:3000"
+ environment:
+ GF_AUTH_GENERIC_OAUTH_ENABLED: "true"
+ GF_AUTH_GENERIC_OAUTH_NAME: "Keycloak"
+ GF_AUTH_GENERIC_OAUTH_ALLOW_SIGN_UP: "true"
+ GF_AUTH_GENERIC_OAUTH_CLIENT_ID: "grafana-ui.app"
+ GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET: "lVPuFWZlOV7yAbV1FIuaM0FOodD7cLTm"
+ GF_AUTH_GENERIC_OAUTH_SCOPES: "openid profile email offline_access roles"
+ GF_AUTH_GENERIC_OAUTH_AUTH_URL: "https://identity.${HTTP_DOMAIN}/realms/onap/protocol/openid-connect/auth"
+ GF_AUTH_GENERIC_OAUTH_TOKEN_URL: "https://identity.${HTTP_DOMAIN}/realms/onap/protocol/openid-connect/token"
+ GF_AUTH_GENERIC_OAUTH_API_URL: "https://identity.${HTTP_DOMAIN}/realms/onap/protocol/openid-connect/userinfo"
+ GF_SERVER_ROOT_URL: "https://grafana.${HTTP_DOMAIN}"
+ GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE: role
+ GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH: contains(resource_access."grafana-ui.app".roles[*], 'grafanaadmin') && 'GrafanaAdmin' || contains(resource_access."grafana-ui.app".roles[*], 'admin') && 'Admin' || contains(resource_access."grafana.app".roles[*], 'editor') && 'Editor' || 'Viewer'
+ GF_AUTH_GENERIC_OAUTH_ALLOW_ASSIGN_GRAFANA_ROLES: true
+ GF_AUTH_GENERIC_OAUTH_ALLOW_ASSIGN_GRAFANA_ADMIN: true
+ GF_AUTH_GENERIC_OAUTH_TLS_SKIP_VERIFY_INSECURE: true
+ GF_LOG_LEVEL: debug
+ labels:
+ traefik.enable: true
+ traefik.http.routers.grafana.entrypoints: websecure
+ traefik.http.routers.grafana.rule: Host(`grafana.${HTTP_DOMAIN}`)
+ traefik.http.routers.grafana.tls: true
+ traefik.http.services.grafana.loadbalancer.server.port: 3000
+ app: "grafana"
+ deploy: "o-ran-sc-smo-oam-pm"
+ solution: "o-ran-sc-smo"
+ networks:
+ - dmz
+
+networks:
+ dmz:
+ external: true
docker compose -p influx -f docker-compose-influxdb_gen.yaml up -d
}
+setup_grafana() {
+ envsubst < docker-compose-grafana.yaml > docker-compose-grafana_gen.yaml
+ docker compose -p grafana -f docker-compose-grafana.yaml up -d
+}
+
create_topics() {
echo "Creating topics: $TOPICS, may take a while ..."
for t in $TOPICS; do
if [ $? -ne 0 ]; then
docker network create $net
else
- echo " Network: $net exits"
+ echo " Network: $net exists"
fi
done
}
setup_pm
check_error $?
+setup_grafana
+check_error $?
\ No newline at end of file
docker compose -p influx -f docker-compose-influxdb_gen.yaml down
docker compose -p pm -f docker-compose_gen.yaml down
+docker compose -p grafana -f docker-compose-grafana_gen.yaml down
echo "Removing influxdb2 config..."
rm -rf ./config/influxdb2