add myoffice scripts
This commit is contained in:
18
myoffice_projects/README.md
Normal file
18
myoffice_projects/README.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
В директории import_mailion содержатся скрипты для импорта пользователей и групп из freeipa в mailion
|
||||||
|
|
||||||
|
В директории house_configs содержатся кастомные конфиги для сервера Mailion
|
||||||
|
|
||||||
|
В директории co_scripts содержатся плагины и скрипты для CO+pgs
|
||||||
|
|
||||||
|
## import_mailion USAGE
|
||||||
|
Изменить 'ldap_admin_password' в export_groups_from_ldap.py и запустить скрипт:
|
||||||
|
|
||||||
|
`
|
||||||
|
mailion.import.sh
|
||||||
|
`
|
||||||
|
|
||||||
|
## house_configs USAGE (deprecated)
|
||||||
|
`
|
||||||
|
cp house_configs/* /srv/docker/house/conf/conf.d
|
||||||
|
docker restart house
|
||||||
|
`
|
||||||
9
myoffice_projects/co_scripts/README.md
Normal file
9
myoffice_projects/co_scripts/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# co_scripts
|
||||||
|
|
||||||
|
Репозиторий для хранения скриптов расширяющих базовый функционал CO\PGS
|
||||||
|
|
||||||
|
| Имя скрипта | Документация | Описание |
|
||||||
|
| ----------------- | ------------------------------------- | ----------------------------------------------- |
|
||||||
|
| pgs_avatar_sync | [README](pgs_avatar_sync/README.md) | Синхронизация фотографий профиля из IPA |
|
||||||
|
| pgs_group_sync | [README](pgs_group_sync/README.md) | Синхронизация групп пользователей из IPA |
|
||||||
|
| pgs_ldap_provider | [README](pgs_ldap_provider/README.md) | KeyCloak провайдер PGS для синхронизации с LDAP |
|
||||||
10
myoffice_projects/co_scripts/pgs_avatar_sync/Dockerfile
Normal file
10
myoffice_projects/co_scripts/pgs_avatar_sync/Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
FROM python:3.11-alpine
|
||||||
|
ARG FLASK_PORT=8085
|
||||||
|
ENV FLASK_PORT=$FLASK_PORT
|
||||||
|
WORKDIR /opt/pgs_avatar
|
||||||
|
COPY . /opt/pgs_avatar
|
||||||
|
RUN python -m venv venv && \
|
||||||
|
source venv/bin/activate && \
|
||||||
|
pip install -U -r requirements.txt
|
||||||
|
EXPOSE $FLASK_PORT
|
||||||
|
CMD ["/bin/sh", "-c", "source venv/bin/activate; flask run --host 0.0.0.0 --port ${FLASK_PORT}"]
|
||||||
52
myoffice_projects/co_scripts/pgs_avatar_sync/README.md
Normal file
52
myoffice_projects/co_scripts/pgs_avatar_sync/README.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# PGS_AVATAR_SYNC
|
||||||
|
|
||||||
|
Скрипт использует Flask для REST API.
|
||||||
|
|
||||||
|
Позволяет при входе пользователей в CO обращаться на IPA сервер для получения фотографии пользователя, а затем, при необходимости, отправляет её в COAPI для обновления в профиле пользователя.
|
||||||
|
|
||||||
|
## Переменные окружения
|
||||||
|
|
||||||
|
| Название | Значение по умолчанию | Описание |
|
||||||
|
| ------------ | ---------------------------------------- | --------------------------------------------------- |
|
||||||
|
| CO_API_URL | "https://coapi.hyperus.team/api/v1/auth" | Конечная точка COAPI для работы с "profile/picture" |
|
||||||
|
| IPA_ADDRESS | ipa.hyperus.team | Адрес сервера IPA |
|
||||||
|
| IPA_LOGIN | automated.carbon | Учетная запись подключения к IPA |
|
||||||
|
| IPA_PASSWORD | - | Пароль учетной записи подключения к IPA |
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
1. Собрать образ и запустить контейнер на сервере CO.
|
||||||
|
```bash
|
||||||
|
docker build . --build-arg FLASK_PORT=8085 --tag pgs_avatar_sync:0.0.1
|
||||||
|
docker run -d -e IPA_PASSWORD="securepassword" --name pgs_avatar_sync --network host --restart always pgs_avatar_sync:0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Добавить дополнительную обработку на стороне `openresty-lb-core-auth`:
|
||||||
|
|
||||||
|
#### `/opt/openresty/nginx/conf/co/lua/auth/co_auth_login.lua`
|
||||||
|
|
||||||
|
Ищем строку в конце файла (~60):
|
||||||
|
```lua
|
||||||
|
ngx.say(cjson.encode({ success = "true", token = token }));
|
||||||
|
```
|
||||||
|
|
||||||
|
Добавляем перед ней:
|
||||||
|
```lua
|
||||||
|
-- Send data for update avatars
|
||||||
|
local httpc = http:new();
|
||||||
|
local request = {
|
||||||
|
method = "POST",
|
||||||
|
body = cjson.encode({ login = login, token = token }),
|
||||||
|
headers = {
|
||||||
|
["Content-Type"] = "application/json; charset utf-8"
|
||||||
|
},
|
||||||
|
ssl_verify = false
|
||||||
|
};
|
||||||
|
--- Необходимо указать корректный порт, FLASK_PORT переданный при сборке
|
||||||
|
local avatar_res, avatar_err = httpc:request_uri("http://172.17.0.1:8085/avatar", request);
|
||||||
|
if not avatar_res then
|
||||||
|
ngx.log(ngx.ERR, "Request failed: ", avatar_err)
|
||||||
|
end
|
||||||
|
httpc:close();
|
||||||
|
ngx.log(ngx_INFO, "Update avatar for <" .. login .. ">.");
|
||||||
|
```
|
||||||
8
myoffice_projects/co_scripts/pgs_avatar_sync/app.py
Normal file
8
myoffice_projects/co_scripts/pgs_avatar_sync/app.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from flask import Flask
|
||||||
|
from flask_restful import Api
|
||||||
|
from modules.flask_view import PGSAvatarListener
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
api = Api(app)
|
||||||
|
|
||||||
|
api.add_resource(PGSAvatarListener, "/avatar", methods=["POST"])
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import base64
|
||||||
|
import os
|
||||||
|
import python_freeipa
|
||||||
|
import requests
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings = warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
|
class PGSAvatarModule:
|
||||||
|
def __init__(self):
|
||||||
|
self.CO_API_URL = os.getenv("COAPI_URL", "https://coapi.hyperus.team/api/v1/auth")
|
||||||
|
self.IPA_ADDRESS = os.getenv("IPA_ADDRESS", "ipa01.hyperus.team")
|
||||||
|
self.IPA_LOGIN = os.getenv("IPA_LOGIN", "automated.carbon")
|
||||||
|
self.IPA_PASSWORD = os.getenv("IPA_PASSWORD")
|
||||||
|
|
||||||
|
def get_avatar_ipa(self, login) -> str:
|
||||||
|
ipa_user = {}
|
||||||
|
ipa_user_avatar = ""
|
||||||
|
ipa_client = python_freeipa.ClientMeta(host=self.IPA_ADDRESS, verify_ssl=False)
|
||||||
|
ipa_client.login(self.IPA_LOGIN, self.IPA_PASSWORD)
|
||||||
|
ipa_users = ipa_client.user_find(o_uid=login)
|
||||||
|
ipa_client.logout()
|
||||||
|
if ipa_users["count"] == 1:
|
||||||
|
ipa_user = ipa_users["result"][0]
|
||||||
|
if "jpegphoto" in ipa_user:
|
||||||
|
ipa_user_avatar = ipa_user["jpegphoto"][-1]["__base64__"]
|
||||||
|
else:
|
||||||
|
return "User doesnt have avatar photo", 406
|
||||||
|
else:
|
||||||
|
return "User not found", 404
|
||||||
|
return base64.b64decode(ipa_user_avatar), 200
|
||||||
|
|
||||||
|
def get_avatar_pgs(self, token) -> str:
|
||||||
|
header = {"X-co-auth-token": token}
|
||||||
|
request = requests.get(url=f"{self.CO_API_URL}/profile/picture", headers=header)
|
||||||
|
if request.status_code == 200:
|
||||||
|
return request.content, 200
|
||||||
|
elif request.status_code == 204:
|
||||||
|
return "Avatar not exist", 204
|
||||||
|
return "Bad response from COAPI", 400
|
||||||
|
|
||||||
|
def update_avatar(self, login, token, photo) -> list:
|
||||||
|
header = {"X-co-auth-token": token}
|
||||||
|
file = [("file", ("avatar.jpg", photo, "image/jpeg"))]
|
||||||
|
request = requests.post(url=f"{self.CO_API_URL}/profile/picture", headers=header, data={}, files=file)
|
||||||
|
if request.status_code == 200:
|
||||||
|
return f"Avatar has been updated for user <{login}>", 200
|
||||||
|
return "Something bad with update process", 401
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
from flask_restful import reqparse, Resource
|
||||||
|
from .flask_model import PGSAvatarModule
|
||||||
|
|
||||||
|
class PGSAvatarListener(Resource):
|
||||||
|
parser = reqparse.RequestParser()
|
||||||
|
def get(self):
|
||||||
|
return "Method not allowed", 405
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
return "Method not allowed", 405
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
self.parser.add_argument("login")
|
||||||
|
self.parser.add_argument("token")
|
||||||
|
args = self.parser.parse_args()
|
||||||
|
if not args["login"] or not args["token"]:
|
||||||
|
return "Not correct query", 400
|
||||||
|
login = args["login"].split("@")[0]
|
||||||
|
token = args["token"]
|
||||||
|
avatar_module = PGSAvatarModule()
|
||||||
|
ipa_photo = avatar_module.get_avatar_ipa(login)
|
||||||
|
pgs_photo = avatar_module.get_avatar_pgs(token)
|
||||||
|
if 400 in ipa_photo:
|
||||||
|
return ipa_photo
|
||||||
|
if 400 in pgs_photo:
|
||||||
|
return pgs_photo
|
||||||
|
if ipa_photo != pgs_photo:
|
||||||
|
return avatar_module.update_avatar(login, token, ipa_photo[0])
|
||||||
|
else:
|
||||||
|
return f"Avatar photo of user <{login}> is actual", 208
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
flask
|
||||||
|
flask-restful
|
||||||
|
pip
|
||||||
|
python-freeipa
|
||||||
|
requests
|
||||||
12
myoffice_projects/co_scripts/pgs_group_sync/Dockerfile
Normal file
12
myoffice_projects/co_scripts/pgs_group_sync/Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
FROM python:3.10-alpine
|
||||||
|
|
||||||
|
WORKDIR /srv/hyperus_apps/pgs_sync
|
||||||
|
COPY group_sync_pgs.py ./
|
||||||
|
|
||||||
|
RUN python3 -m venv venv && \
|
||||||
|
source venv/bin/activate && \
|
||||||
|
pip install \
|
||||||
|
python-freeipa==1.0.6 \
|
||||||
|
requests
|
||||||
|
|
||||||
|
CMD ["/bin/sh", "-c", "source venv/bin/activate; python ./group_sync_pgs.py"]
|
||||||
30
myoffice_projects/co_scripts/pgs_group_sync/README.md
Normal file
30
myoffice_projects/co_scripts/pgs_group_sync/README.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# PGS_GROUP_SYNC
|
||||||
|
|
||||||
|
- Скрипт синхронизирует группы в PGS из IPA
|
||||||
|
- Актуализирует состав групп пользователей
|
||||||
|
|
||||||
|
## Переменные окружения
|
||||||
|
| Название | Значение по умолчанию | Описание |
|
||||||
|
| --------------------- | ------------------------------------- | ---------------------------------------------- |
|
||||||
|
| IPA_ADDRESS | "ipa01.hyperus.team" | Адрес сервера IPA |
|
||||||
|
| IPA_GROUP_ATTR | "description" | Атрибут используемый в качестве имени группы |
|
||||||
|
| IPA_USERNAME | "automated.carbon" | Учетная запись подключения к IPA |
|
||||||
|
| IPA_PASSWORD | | Пароль учетной записи подключения к IPA |
|
||||||
|
| PGS_ADMINAPI_URL | "https://admin.hyperus.team/adminapi" | Адрес AdminAPI PGS |
|
||||||
|
| PGS_ADMINAPI_PASSWORD | | Пароль учетной записи с правами администратора |
|
||||||
|
| PGS_ADMINAPI_TENANT | "default" | Тенант к которому принадлежит домен |
|
||||||
|
| PGS_ADMINAPI_USERNAME | "admin@hyperus.team" | Учетная запись с правами администратора |
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
1. Собрать образ и запустить контейнер.
|
||||||
|
После аргумента `-e` указать переменные окружения и их значения
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build . --tag pgs_group_sync:0.0.1
|
||||||
|
docker run -d -e IPA_PASSWORD="securepassword" -e PGS_ADMINAPI_PASSWORD="securepassword" --name pgs_group_sync pgs_group_sync:0.0.1
|
||||||
|
```
|
||||||
|
2. Добавить в cron задачу по запуску контейнера с необходимым интервалом.
|
||||||
|
```
|
||||||
|
*/5 * * * * docker start pgs_group_sync 2>%1 1>/dev/null
|
||||||
|
```
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
apiVersion: "batch/v1"
|
||||||
|
kind: "CronJob"
|
||||||
|
metadata:
|
||||||
|
name: "groupsync-cronjob"
|
||||||
|
spec:
|
||||||
|
concurrencyPolicy: "Forbid"
|
||||||
|
failedJobsHistoryLimit: 1
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
backoffLimit: 0
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: "group_sync_pgs"
|
||||||
|
image: "nexus.hyperus.team/groupsync:latest"
|
||||||
|
imagePullPolicy: "IfNotPresent"
|
||||||
|
env:
|
||||||
|
- name: "PGS_ADMINAPI_PASSWORD"
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: "infrastructure/pgs"
|
||||||
|
key: "ADMIN_PASSWORD"
|
||||||
|
- name: "IPA_PASSWORD"
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: "infrastructure/service_accounts/carbon"
|
||||||
|
key: "password"
|
||||||
|
restartPolicy: "Never"
|
||||||
|
schedule: "*/5 * * * *"
|
||||||
147
myoffice_projects/co_scripts/pgs_group_sync/group_sync_pgs.py
Normal file
147
myoffice_projects/co_scripts/pgs_group_sync/group_sync_pgs.py
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import python_freeipa as freeipa
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
|
|
||||||
|
class GroupSyncPGS:
|
||||||
|
"""
|
||||||
|
Реализует:
|
||||||
|
- Получение списка групп с участниками из FreeIPA
|
||||||
|
- Получение списка групп с участниками из PGS
|
||||||
|
- Получение списка пользователей из PGS
|
||||||
|
- Сравнение групп и членства участников
|
||||||
|
- Добавление групп и участников в PGS
|
||||||
|
- Удаление групп и участников в PGS
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.ipa_address = os.environ.get("IPA_ADDRESS", "ipa01.hyperus.team")
|
||||||
|
self.ipa_group_attr = os.environ.get("IPA_GROUP_ATTR", "description")
|
||||||
|
self.ipa_password = os.environ.get("IPA_PASSWORD")
|
||||||
|
self.ipa_username = os.environ.get("IPA_USERNAME", "automated.carbon")
|
||||||
|
self.pgs_adminapi_address = os.environ.get("PGS_ADMINAPI_URL", "https://admin.hyperus.team/adminapi")
|
||||||
|
self.pgs_adminapi_password = os.environ.get("PGS_ADMINAPI_PASSWORD")
|
||||||
|
self.pgs_adminapi_tenant = os.environ.get("PGS_ADMINAPI_TENANT", "default")
|
||||||
|
self.pgs_adminapi_username = os.environ.get("PGS_ADMINAPI_USERNAME", "admin@hyperus.team")
|
||||||
|
|
||||||
|
def pgs_auth(self) -> None:
|
||||||
|
pgs_credentials = {"username": self.pgs_adminapi_username, "password": self.pgs_adminapi_password}
|
||||||
|
result = requests.post(url=f"{self.pgs_adminapi_address}/auth", data=pgs_credentials, verify=False)
|
||||||
|
if result.status_code == 200:
|
||||||
|
self.pgs_adminapi_token = result.json()["token"]
|
||||||
|
else:
|
||||||
|
raise ConnectionRefusedError("Authentication failed")
|
||||||
|
self.__pgs_adminapi_header = {"Authorization": self.pgs_adminapi_token}
|
||||||
|
|
||||||
|
def get_ipa_groups(self) -> dict:
|
||||||
|
ipa_groups_formated = {}
|
||||||
|
ipa_client = freeipa.ClientMeta(host=self.ipa_address, verify_ssl=False)
|
||||||
|
ipa_client.login(self.ipa_username, self.ipa_password)
|
||||||
|
ipa_groups_find = ipa_client.group_find()
|
||||||
|
ipa_client.logout()
|
||||||
|
ipa_groups_find = list(filter( lambda ipa_group: "member_user" in ipa_group.keys(), ipa_groups_find["result"] ))
|
||||||
|
for ipa_group in ipa_groups_find:
|
||||||
|
ipa_group_name = "".join(ipa_group[self.ipa_group_attr])
|
||||||
|
ipa_groups_formated[ipa_group_name] = ["{}@hyperus.team".format(user) for user in ipa_group["member_user"]]
|
||||||
|
return ipa_groups_formated
|
||||||
|
|
||||||
|
def get_pgs_groups(self) -> dict:
|
||||||
|
pgs_groups_formated = {}
|
||||||
|
response = requests.get(url=f"{self.pgs_adminapi_address}/tenants/{self.pgs_adminapi_tenant}/groups", headers=self.__pgs_adminapi_header, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
pgs_groups_finded = response.json()["groups"]
|
||||||
|
else:
|
||||||
|
raise Exception("PGS API: can't get groups list")
|
||||||
|
for pgs_group in pgs_groups_finded:
|
||||||
|
pgs_groups_formated[pgs_group["name"]] = {}
|
||||||
|
pgs_groups_formated[pgs_group["name"]]["id"] = pgs_group["id"]
|
||||||
|
pgs_groups_formated[pgs_group["name"]]["members"] = ["{}".format(user["username"]) for user in pgs_group["users"]]
|
||||||
|
return pgs_groups_formated
|
||||||
|
|
||||||
|
def get_pgs_users(self) -> dict:
|
||||||
|
pgs_users_formated = {}
|
||||||
|
response = requests.get(url=f"{self.pgs_adminapi_address}/tenants/{self.pgs_adminapi_tenant}/users", headers=self.__pgs_adminapi_header, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
pgs_users_finded = response.json()["users"]
|
||||||
|
else:
|
||||||
|
raise Exception("PGS API: can't get users list")
|
||||||
|
for pgs_user in pgs_users_finded:
|
||||||
|
pgs_users_formated[pgs_user["username"]] = pgs_user["id"]
|
||||||
|
return pgs_users_formated
|
||||||
|
|
||||||
|
def compare_groups(self, source={}, destination={}, pgs_users={}) -> None:
|
||||||
|
if not source:
|
||||||
|
source = self.get_ipa_groups()
|
||||||
|
if not destination:
|
||||||
|
destination = self.get_pgs_groups()
|
||||||
|
if not pgs_users:
|
||||||
|
pgs_users = self.get_pgs_users()
|
||||||
|
|
||||||
|
to_create_groups = list(filter(lambda group: group not in destination, source))
|
||||||
|
to_delete_groups = list(filter(lambda group: group not in source, destination))
|
||||||
|
for group in to_create_groups:
|
||||||
|
group_members = [pgs_users[username] for username in source[group]]
|
||||||
|
self.create_pgs_group(group, group_members)
|
||||||
|
for group in to_delete_groups:
|
||||||
|
self.delete_pgs_group(destination[group]["id"])
|
||||||
|
|
||||||
|
def compare_members(self, source={}, destination={}, pgs_users={}) -> None:
|
||||||
|
if not source:
|
||||||
|
source = self.get_ipa_groups()
|
||||||
|
if not destination:
|
||||||
|
destination = self.get_pgs_groups()
|
||||||
|
if not pgs_users:
|
||||||
|
pgs_users = self.get_pgs_users()
|
||||||
|
|
||||||
|
for group in source:
|
||||||
|
to_create_membership = list(filter( lambda member: member not in destination[group]["members"], source[group] ))
|
||||||
|
to_remove_membership = list(filter( lambda member: member not in source[group], destination[group]["members"] ))
|
||||||
|
if len(to_create_membership) > 0:
|
||||||
|
group_members = [pgs_users[username] for username in to_create_membership]
|
||||||
|
response = self.set_pgs_group_members("POST", destination[group]["id"], group_members)
|
||||||
|
if response.status_code == 200:
|
||||||
|
print("Users has been added to group <{}>".format(group))
|
||||||
|
else:
|
||||||
|
raise Exception("Error during adding members to group <{}>".format(group))
|
||||||
|
if len(to_remove_membership) > 0:
|
||||||
|
group_members = [pgs_users[username] for username in to_remove_membership]
|
||||||
|
response = self.set_pgs_group_members("DELETE", destination[group]["id"], group_members)
|
||||||
|
if response.status_code == 200:
|
||||||
|
print("Users has been removed from group <{}>".format(group))
|
||||||
|
else:
|
||||||
|
raise Exception("Error during removing members from group <{}>".format(group))
|
||||||
|
|
||||||
|
def create_pgs_group(self, pgs_group_name, pgs_users={}):
|
||||||
|
pgs_group_data = {"name": pgs_group_name, "users": pgs_users}
|
||||||
|
response = requests.post(url=f"{self.pgs_adminapi_address}/tenants/{self.pgs_adminapi_tenant}/groups", headers=self.__pgs_adminapi_header, data=pgs_group_data, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
print("Group <{}> was created".format(pgs_group_name))
|
||||||
|
else:
|
||||||
|
raise Exception("Error during creation group <{}>".format(pgs_group_name))
|
||||||
|
return response
|
||||||
|
|
||||||
|
def delete_pgs_group(self, pgs_group_name):
|
||||||
|
response = requests.delete(url=f"{self.pgs_adminapi_address}/tenants/{self.pgs_adminapi_tenant}/groups/{pgs_group_name}", headers=self.__pgs_adminapi_header, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
print("Group <{}> was deleted".format(pgs_group_name))
|
||||||
|
else:
|
||||||
|
raise Exception("Error during removing group <{}>".format(pgs_group_name))
|
||||||
|
return response
|
||||||
|
|
||||||
|
def set_pgs_group_members(self, method, pgs_group_id, pgs_users={}):
|
||||||
|
pgs_data = {"users": pgs_users}
|
||||||
|
response = requests.request(method, f"{self.pgs_adminapi_address}/tenants/{self.pgs_adminapi_tenant}/groups/{pgs_group_id}/users", headers=self.__pgs_adminapi_header, data=pgs_data, verify=False)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sync_manager = GroupSyncPGS()
|
||||||
|
sync_manager.pgs_auth()
|
||||||
|
pgs_users = sync_manager.get_pgs_users()
|
||||||
|
ipa_groups = sync_manager.get_ipa_groups()
|
||||||
|
pgs_groups = sync_manager.get_pgs_groups()
|
||||||
|
sync_manager.compare_groups(ipa_groups, pgs_groups, pgs_users)
|
||||||
|
sync_manager.compare_members(ipa_groups, pgs_users=pgs_users)
|
||||||
20
myoffice_projects/co_scripts/pgs_ldap_provider/README.md
Normal file
20
myoffice_projects/co_scripts/pgs_ldap_provider/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# PGS LDAP PROVIDER
|
||||||
|
|
||||||
|
Провайдер для KeyCloak (компонент PGS) отвечающий за синхронизацию пользователей по LDAP.
|
||||||
|
|
||||||
|
## Протестирован
|
||||||
|
- PGS 2.4
|
||||||
|
- PGS 2.5
|
||||||
|
- PGS 2.6
|
||||||
|
|
||||||
|
## Изменения относительно PGS
|
||||||
|
- Добавлена возможность указания LDAP атрибутов для полей ФИО
|
||||||
|
- Добавлена возможность указания дополнительных атрибутов (город, должность, департамент)
|
||||||
|
- Исправлено заполнение резервной почты тем же адресом что и основная
|
||||||
|
- Исправлено поведение, при котором после внесения изменений в провайдер приходилось удалять и заново добавлять синхронизировать пользователей
|
||||||
|
- При изменении маппинга атрибутов не требуется пересоздание провайдера
|
||||||
|
|
||||||
|
## Сборка
|
||||||
|
- Установить Maven
|
||||||
|
- Выполнить `mvn clean package`
|
||||||
|
- В каталоге `target` будет собранный `pgs-ldap-provider.jar`
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#Generated by Maven
|
||||||
|
#Wed Apr 26 16:49:11 UTC 2023
|
||||||
|
groupId=team.hyperus
|
||||||
|
artifactId=pgs-ldap-provider
|
||||||
|
version=0.0.1
|
||||||
99
myoffice_projects/co_scripts/pgs_ldap_provider/pom.xml
Normal file
99
myoffice_projects/co_scripts/pgs_ldap_provider/pom.xml
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>team.hyperus</groupId>
|
||||||
|
<artifactId>pgs-ldap-provider</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
<keycloak.version>13.0.1</keycloak.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-server-spi</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-server-spi-private</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-kerberos-federation</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-ldap-federation</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>resteasy-jaxrs</artifactId>
|
||||||
|
<version>3.9.0.Final</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.logging</groupId>
|
||||||
|
<artifactId>jboss-logging</artifactId>
|
||||||
|
<version>3.4.1.Final</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.spec.javax.transaction</groupId>
|
||||||
|
<artifactId>jboss-transaction-api_1.2_spec</artifactId>
|
||||||
|
<version>1.1.1.Final</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.spec.javax.ejb</groupId>
|
||||||
|
<artifactId>jboss-ejb-api_3.2_spec</artifactId>
|
||||||
|
<version>2.0.0.Final</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<finalName>pgs-ldap-provider</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package team.hyperus;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
public class EuclidConnector {
|
||||||
|
private static final String EUCLID_URL = "http://euclid:8852";
|
||||||
|
private static final String EUCLID_BASE_LOGIN = System.getenv("KEYCLOAK_USER");
|
||||||
|
private static final String EUCLID_BASE_PASS = System.getenv("KEYCLOAK_PASSWORD");
|
||||||
|
private static final String URL_ENC_HEADER = "application/x-www-form-urlencoded";
|
||||||
|
private String usersURL;
|
||||||
|
private static final Logger logger = Logger.getLogger(EuclidConnector.class);
|
||||||
|
private static final HttpClient httpClient = HttpClient.newBuilder()
|
||||||
|
.version(HttpClient.Version.HTTP_1_1)
|
||||||
|
.connectTimeout(Duration.ofSeconds(60L)).build();
|
||||||
|
|
||||||
|
public EuclidConnector(String realm) {
|
||||||
|
this.usersURL = String.format("%s/tenants/%s/users/", EUCLID_URL, realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String constructAuthJSON() {
|
||||||
|
return String.format("{\"login\": \"%s\", \"password\": \"%s\"}", EUCLID_BASE_LOGIN, EUCLID_BASE_PASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createUser(String id, String username, String quota, String recoveryEmail) {
|
||||||
|
HashMap<String, String> userParams = new HashMap<>();
|
||||||
|
userParams.put("id", id);
|
||||||
|
userParams.put("username", username);
|
||||||
|
userParams.put("email", username);
|
||||||
|
userParams.put("quota", quota);
|
||||||
|
userParams.put("recovery_email", recoveryEmail);
|
||||||
|
userParams.put("basic_auth", constructAuthJSON());
|
||||||
|
String requestBody = userParams.entrySet().stream()
|
||||||
|
.map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
|
||||||
|
.collect(Collectors.joining("&"));
|
||||||
|
HttpRequest request = HttpRequest.newBuilder().POST(HttpRequest.BodyPublishers.ofString(requestBody))
|
||||||
|
.uri(URI.create(this.usersURL)).headers("Content-Type", URL_ENC_HEADER)
|
||||||
|
.build();
|
||||||
|
try {
|
||||||
|
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
} catch (InterruptedException | IOException e) {
|
||||||
|
logger.infof("Can't create user in euclid %s", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package team.hyperus;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.ejb.Remove;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.component.ComponentModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.storage.StorageId;
|
||||||
|
import org.keycloak.storage.adapter.InMemoryUserAdapter;
|
||||||
|
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||||
|
import org.keycloak.storage.ldap.LDAPUtils;
|
||||||
|
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||||
|
import org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore;
|
||||||
|
import org.keycloak.storage.ldap.mappers.LDAPMappersComparator;
|
||||||
|
import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
|
||||||
|
|
||||||
|
public class PgsStorageProvider extends LDAPStorageProvider {
|
||||||
|
protected Long quota = 0L;
|
||||||
|
protected String domain = "";
|
||||||
|
private LDAPMappersComparator ldapMappersComparator;
|
||||||
|
private static final Logger logger = Logger.getLogger(PgsStorageProvider.class);
|
||||||
|
|
||||||
|
public PgsStorageProvider(PgsStorageProviderFactory factory, KeycloakSession session, ComponentModel model, LDAPIdentityStore ldapIdentityStore) {
|
||||||
|
super(factory, session, model, ldapIdentityStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected UserModel importUserFromLDAP(KeycloakSession session, RealmModel realm, LDAPObject ldapUser) {
|
||||||
|
EuclidConnector euclidClient = new EuclidConnector(realm.getName());
|
||||||
|
List<GroupModel> groups = (List)realm.getGroupsStream().collect(Collectors.toList());
|
||||||
|
if (this.quota == 0L) {
|
||||||
|
Iterator var6 = groups.iterator();
|
||||||
|
while(var6.hasNext()) {
|
||||||
|
GroupModel gr = (GroupModel)var6.next();
|
||||||
|
if (gr.getAttributes().containsKey("default")) {
|
||||||
|
this.quota = Long.parseLong((String)((List)gr.getAttributes().get("quota")).get(0));
|
||||||
|
this.domain = gr.getName();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String tmp = "";
|
||||||
|
if (!ldapUser.getAttributeAsString(this.ldapIdentityStore.getConfig().getUsernameLdapAttribute()).contains("@")) {
|
||||||
|
tmp = ldapUser.getAttributeAsString(this.ldapIdentityStore.getConfig().getUsernameLdapAttribute()) + "@" + this.domain;
|
||||||
|
Set attributeSet = new HashSet();
|
||||||
|
attributeSet.add(tmp);
|
||||||
|
ldapUser.setAttribute(this.ldapIdentityStore.getConfig().getUsernameLdapAttribute(), attributeSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
String ldapUsername = LDAPUtils.getUsername(ldapUser, this.ldapIdentityStore.getConfig());
|
||||||
|
if (!ldapUsername.contains("@")) {
|
||||||
|
ldapUsername = ldapUsername + "@" + this.domain;
|
||||||
|
}
|
||||||
|
HashSet imported;
|
||||||
|
if (ldapUser.getAttributeAsString("sn") == null || ldapUser.getAttributeAsString("sn").equals("")) {
|
||||||
|
imported = new HashSet();
|
||||||
|
imported.add(ldapUsername.split("@")[0]);
|
||||||
|
ldapUser.setAttribute("sn", imported);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ldapUser.getAttributeAsString("mail") == null || ldapUser.getAttributeAsString("mail").equals("")) {
|
||||||
|
imported = new HashSet();
|
||||||
|
imported.add(ldapUsername);
|
||||||
|
ldapUser.setAttribute("mail", imported);
|
||||||
|
}
|
||||||
|
|
||||||
|
LDAPUtils.checkUuid(ldapUser, this.ldapIdentityStore.getConfig());
|
||||||
|
imported = null;
|
||||||
|
UserModel finalImported;
|
||||||
|
if (this.model.isImportEnabled()) {
|
||||||
|
UserModel existingLocalUser = session.userLocalStorage().searchForUserByUserAttributeStream(realm, "LDAP_ID", ldapUser.getUuid()).findFirst().orElse(null);
|
||||||
|
if (existingLocalUser != null) {
|
||||||
|
finalImported = existingLocalUser;
|
||||||
|
session.userCache().evict(realm, existingLocalUser);
|
||||||
|
} else {
|
||||||
|
finalImported = session.userLocalStorage().addUser(realm, ldapUsername);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
InMemoryUserAdapter adapter = new InMemoryUserAdapter(session, realm, (new StorageId(this.model.getId(), ldapUsername)).getId());
|
||||||
|
adapter.addDefaults();
|
||||||
|
finalImported = adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalImported.setEnabled(true);
|
||||||
|
this.ldapMappersComparator = new LDAPMappersComparator(this.getLdapIdentityStore().getConfig());
|
||||||
|
realm.getComponentsStream(this.model.getId(), LDAPStorageMapper.class.getName()).sorted(this.ldapMappersComparator.sortDesc()).forEachOrdered((mapperModel) -> {
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.tracef("Using mapper %s during import user from LDAP", mapperModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
LDAPStorageMapper ldapMapper = this.mapperManager.getMapper(mapperModel);
|
||||||
|
ldapMapper.onImportUserFromLDAP(ldapUser, finalImported, realm, true);
|
||||||
|
});
|
||||||
|
String userDN = ldapUser.getDn().toString();
|
||||||
|
if (this.model.isImportEnabled()) {
|
||||||
|
finalImported.setFederationLink(this.model.getId());
|
||||||
|
}
|
||||||
|
finalImported.setSingleAttribute("LDAP_ID", ldapUser.getUuid());
|
||||||
|
finalImported.setSingleAttribute("LDAP_ENTRY_DN", userDN);
|
||||||
|
finalImported.setSingleAttribute("quota", this.quota.toString());
|
||||||
|
finalImported.setSingleAttribute("recovery_email", (ldapUser.getUuid() + "@not-set-recovery.mail"));
|
||||||
|
finalImported.setSingleAttribute("is_admin_user", "0");
|
||||||
|
finalImported.setSingleAttribute("eula_accept_required", "0");
|
||||||
|
if (!this.model.get("cnSplitNames", false)){
|
||||||
|
finalImported.setFirstName(ldapUser.getAttributeAsString(this.model.get("fnLDAPAttribute")));
|
||||||
|
finalImported.setLastName(ldapUser.getAttributeAsString(this.model.get("lnLDAPAttribute")));
|
||||||
|
} else {
|
||||||
|
String cn = ldapUser.getAttributeAsString("cn");
|
||||||
|
finalImported.setLastName(ldapUser.getAttributeAsString("sn"));
|
||||||
|
if (finalImported.getLastName() == null) {
|
||||||
|
finalImported.setLastName(ldapUsername.split("@")[0]);
|
||||||
|
}
|
||||||
|
if ((cn.split(" ")).length == 3) {
|
||||||
|
finalImported.setFirstName(cn.split(" ")[1]);
|
||||||
|
finalImported.setSingleAttribute("middle_name", cn.split(" ")[2]);
|
||||||
|
finalImported.setLastName(cn.split(" ")[0]);
|
||||||
|
}
|
||||||
|
if (finalImported.getFirstName() == null) {
|
||||||
|
finalImported.setFirstName(ldapUsername.split("@")[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.getLdapIdentityStore().getConfig().isTrustEmail()) {
|
||||||
|
((UserModel)finalImported).setEmailVerified(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserModel proxy = this.proxy(realm, (UserModel)finalImported, ldapUser, false);
|
||||||
|
euclidClient.createUser(((UserModel)finalImported).getId(), ((UserModel)finalImported).getUsername(), Long.toString(this.quota), this.replaceDomain("admin", this.domain));
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String replaceDomain(String username, String newDomain) {
|
||||||
|
return username.split("@")[0] + "@" + newDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Remove
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
package team.hyperus;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.component.ComponentModel;
|
||||||
|
import org.keycloak.models.GroupModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.models.KeycloakSessionTask;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
import org.keycloak.provider.ServerInfoAwareProviderFactory;
|
||||||
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
|
import org.keycloak.storage.ldap.LDAPIdentityStoreRegistry;
|
||||||
|
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||||
|
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
|
||||||
|
import org.keycloak.storage.ldap.LDAPUtils;
|
||||||
|
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||||
|
import org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore;
|
||||||
|
import org.keycloak.storage.ldap.mappers.LDAPConfigDecorator;
|
||||||
|
import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
|
||||||
|
import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapper;
|
||||||
|
import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapperFactory;
|
||||||
|
import org.keycloak.storage.user.SynchronizationResult;
|
||||||
|
|
||||||
|
public class PgsStorageProviderFactory extends LDAPStorageProviderFactory implements ServerInfoAwareProviderFactory {
|
||||||
|
private LDAPIdentityStoreRegistry ldapStoreRegistry;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "pgsldapnew";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return "PgsLDAPNew";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LDAPStorageProvider create(KeycloakSession session, ComponentModel model) {
|
||||||
|
Map<ComponentModel, LDAPConfigDecorator> configDecorators = getLDAPConfigDecorators(session, model);
|
||||||
|
|
||||||
|
LDAPIdentityStore ldapIdentityStore = this.ldapStoreRegistry.getLdapStore(session, model,
|
||||||
|
configDecorators);
|
||||||
|
return new PgsStorageProvider(this, session, model, ldapIdentityStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ProviderConfigProperty> getConfigProperties() {
|
||||||
|
List<ProviderConfigProperty> props = new LinkedList<>();
|
||||||
|
props.add(new ProviderConfigProperty("vendor", "LDAP Vendor", "ActiveDirectory, RHDS", "List", "rhds", "rhds", "ad"));
|
||||||
|
props.add(new ProviderConfigProperty("connectionUrl", "LDAP URL", "URL address for LDAP. eg: ldap://10.0.0.1", "String", "ldap://"));
|
||||||
|
props.add(new ProviderConfigProperty("usersDn", "Base search DN", "DN for searching users", "String", ""));
|
||||||
|
props.add(new ProviderConfigProperty("userObjectClasses", "Object classes for users", "AD: person, organizationalPerson, user || RHDS: inetOrgPerson, organizationalPerson", "String", "inetOrgPerson, organizationalPerson"));
|
||||||
|
props.add(new ProviderConfigProperty("bindDn", "Bind DN account", "Username or full DN to bind account", "String", ""));
|
||||||
|
props.add(new ProviderConfigProperty("bindCredential", "Bind account password", "Password of bind account", "Password", "", true));
|
||||||
|
props.add(new ProviderConfigProperty("uuidLDAPAttribute", "UUID attribute", "Unique attribute by user", "String", "uid"));
|
||||||
|
props.add(new ProviderConfigProperty("usernameLDAPAttribute", "Username attribute", "Username LDAP attribute", "String", "uid"));
|
||||||
|
props.add(new ProviderConfigProperty("cnSplitNames", "Generate names", "Split CN for First,Last and Middle names", "boolean", "false"));
|
||||||
|
props.add(new ProviderConfigProperty("fnLDAPAttribute", "First name", "LDAP Attribute", "String", "givenname"));
|
||||||
|
props.add(new ProviderConfigProperty("mnLDAPAttribute", "Middle name", "LDAP Attribute", "String", "middlename"));
|
||||||
|
props.add(new ProviderConfigProperty("lnLDAPAttribute", "Last name", "LDAP Attribute", "String", "sn"));
|
||||||
|
props.add(new ProviderConfigProperty("locLDAPAttribute", "Location", "City. LDAP Attribute", "String", "l"));
|
||||||
|
props.add(new ProviderConfigProperty("ouLDAPAttribute", "Department", "Department. LDAP Attribute", "String", "ou"));
|
||||||
|
props.add(new ProviderConfigProperty("posLDAPAttribute", "Job title", "Job title/position. LDAP Attribute", "String", "title"));
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
public ComponentModel getComponentModelByName(RealmModel realm, String name){
|
||||||
|
List<ComponentModel> components = realm.getComponents();
|
||||||
|
for (ComponentModel component : components) {
|
||||||
|
if (component.getName().equals(name)) {
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public ComponentModel createMapper(ComponentModel model, String name, String key){
|
||||||
|
ComponentModel mapperModel;
|
||||||
|
mapperModel = KeycloakModelUtils.createComponentModel(name, model.getId(),
|
||||||
|
UserAttributeLDAPStorageMapperFactory.PROVIDER_ID,
|
||||||
|
LDAPStorageMapper.class.getName(),
|
||||||
|
UserAttributeLDAPStorageMapper.USER_MODEL_ATTRIBUTE, name,
|
||||||
|
UserAttributeLDAPStorageMapper.LDAP_ATTRIBUTE, model.get(key),
|
||||||
|
UserAttributeLDAPStorageMapper.READ_ONLY, "true",
|
||||||
|
UserAttributeLDAPStorageMapper.ALWAYS_READ_VALUE_FROM_LDAP, "true",
|
||||||
|
UserAttributeLDAPStorageMapper.IS_MANDATORY_IN_LDAP, "false");
|
||||||
|
return mapperModel;
|
||||||
|
}
|
||||||
|
public RealmModel updateMappers(RealmModel realm, ComponentModel model) {
|
||||||
|
ComponentModel mapperModel;
|
||||||
|
Map<String, String> mappersMap = new HashMap<String, String>();
|
||||||
|
mappersMap.put("firstName", "fnLDAPAttribute");
|
||||||
|
mappersMap.put("middle_name", "mnLDAPAttribute");
|
||||||
|
mappersMap.put("lastName", "lnLDAPAttribute");
|
||||||
|
mappersMap.put("city", "locLDAPAttribute");
|
||||||
|
mappersMap.put("position", "posLDAPAttribute");
|
||||||
|
mappersMap.put("unit", "ouLDAPAttribute");
|
||||||
|
|
||||||
|
if (!model.get("cnSplitNames", false)) {
|
||||||
|
for(Map.Entry<String, String> mapper : mappersMap.entrySet()){
|
||||||
|
mapperModel = getComponentModelByName(realm, mapper.getKey());
|
||||||
|
if(mapperModel != null){
|
||||||
|
mapperModel.put(UserAttributeLDAPStorageMapper.LDAP_ATTRIBUTE, model.get(mapper.getValue()));
|
||||||
|
realm.updateComponent(mapperModel);
|
||||||
|
} else {
|
||||||
|
realm.addComponentModel(createMapper(model, mapper.getKey(), mapper.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(Map.Entry<String, String> mapper : mappersMap.entrySet()){
|
||||||
|
mapperModel = getComponentModelByName(realm, mapper.getKey());
|
||||||
|
if(mapperModel != null){
|
||||||
|
realm.removeComponent(mapperModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
realm.addComponentModel(createMapper(model, "city", "locLDAPAttribute"));
|
||||||
|
realm.addComponentModel(createMapper(model, "position", "posLDAPAttribute"));
|
||||||
|
realm.addComponentModel(createMapper(model, "unit", "ouLDAPAttribute"));
|
||||||
|
}
|
||||||
|
return (realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCreate(KeycloakSession session, RealmModel realm, ComponentModel model) {
|
||||||
|
model.getConfig().putSingle("editMode", UserStorageProvider.EditMode.UNSYNCED.name());
|
||||||
|
realm = updateMappers(realm, model);
|
||||||
|
|
||||||
|
super.onCreate(session, realm, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdate(KeycloakSession session, RealmModel realm, ComponentModel oldModel, ComponentModel newModel) {
|
||||||
|
realm = updateMappers(realm, newModel);
|
||||||
|
super.onUpdate(session, realm, oldModel, newModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
this.ldapStoreRegistry = new LDAPIdentityStoreRegistry();
|
||||||
|
super.getConfigProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SynchronizationResult importLdapUsers(KeycloakSessionFactory sessionFactory, final String realmId, final ComponentModel fedModel, final List<LDAPObject> ldapUsers) {
|
||||||
|
try {
|
||||||
|
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||||
|
public void run(KeycloakSession session) {
|
||||||
|
Long quota = Long.valueOf(0L);
|
||||||
|
String domain = "";
|
||||||
|
RealmModel currentRealm = session.realms().getRealm(realmId);
|
||||||
|
LDAPStorageProvider ldapFedProvider = (LDAPStorageProvider)session.getProvider(UserStorageProvider.class, fedModel);
|
||||||
|
List<GroupModel> groups = (List<GroupModel>)currentRealm.getGroupsStream().collect(Collectors.toList());
|
||||||
|
String usernameAttr = ldapFedProvider.getLdapIdentityStore().getConfig().getUsernameLdapAttribute();
|
||||||
|
if (quota.longValue() == 0L)
|
||||||
|
for (GroupModel gr : groups) {
|
||||||
|
if (gr.getAttributes().containsKey("default")) {
|
||||||
|
quota = Long.valueOf(Long.parseLong(((List<String>)gr.getAttributes().get("quota")).get(0)));
|
||||||
|
domain = gr.getName();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (LDAPObject ldapUser : ldapUsers) {
|
||||||
|
String ldapUsername = LDAPUtils.getUsername(ldapUser, ldapFedProvider.getLdapIdentityStore().getConfig());
|
||||||
|
if (!ldapUsername.contains("@")) {
|
||||||
|
ldapUsername = ldapUsername + "@" + domain;
|
||||||
|
} else {
|
||||||
|
ldapUsername = ldapUsername.split("@")[0] + domain;
|
||||||
|
}
|
||||||
|
Set<String> set = new HashSet<>();
|
||||||
|
set.add(ldapUsername);
|
||||||
|
ldapUser.setAttribute("mail", set);
|
||||||
|
ldapUser.setAttribute(usernameAttr, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception exception) {}
|
||||||
|
return super.importLdapUsers(sessionFactory, realmId, fedModel, ldapUsers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getOperationalInfo() {
|
||||||
|
Map<String, String> ret = new LinkedHashMap<>();
|
||||||
|
ret.put("custom-ldap", "pgsldapnew");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<jboss-deployment-structure>
|
||||||
|
<deployment>
|
||||||
|
<dependencies>
|
||||||
|
<module name="org.keycloak.keycloak-core" />
|
||||||
|
<module name="org.keycloak.keycloak-server-spi" />
|
||||||
|
<module name="org.keycloak.keycloak-server-spi-private" />
|
||||||
|
<module name="org.keycloak.keycloak-kerberos-federation" />
|
||||||
|
<module name="org.keycloak.keycloak-ldap-federation" />
|
||||||
|
<module name="org.keycloak.keycloak-model-jpa" />
|
||||||
|
<module name="org.keycloak.keycloak-common" />
|
||||||
|
<module name="org.keycloak.keycloak-model-infinispan" />
|
||||||
|
<module name="org.keycloak.keycloak-services" />
|
||||||
|
</dependencies>
|
||||||
|
</deployment>
|
||||||
|
</jboss-deployment-structure>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# SPI class implementation
|
||||||
|
team.hyperus.PgsStorageProviderFactory
|
||||||
94
myoffice_projects/house_configs/autoconfig_psn.conf
Normal file
94
myoffice_projects/house_configs/autoconfig_psn.conf
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
https://autoconfig-app.avroid.tech,
|
||||||
|
https://grpc-app.avroid.tech,
|
||||||
|
http://autoconfig.app.avroid.tech,
|
||||||
|
https://app.avroid.tech/autodiscover,
|
||||||
|
https://app.avroid.tech/Autodiscover
|
||||||
|
{
|
||||||
|
|
||||||
|
tls /etc/pki/tls/certs/bundle_server.crt /etc/pki/tls/private/server.nopass.key
|
||||||
|
|
||||||
|
grpcmeta
|
||||||
|
|
||||||
|
autodiscover {
|
||||||
|
client_type auto
|
||||||
|
clients_configuration {
|
||||||
|
display_name "New Cloud Tech Mail Subsystem"
|
||||||
|
display_short_name NCT
|
||||||
|
smtp_authentication password-cleartext
|
||||||
|
smtp_uri smtp-app.avroid.tech
|
||||||
|
smtp_port 587
|
||||||
|
smtp_ssl STARTTLS
|
||||||
|
imap_authentication password-cleartext
|
||||||
|
imap_uri imap-app.avroid.tech
|
||||||
|
imap_port 143
|
||||||
|
imap_ssl STARTTLS
|
||||||
|
addressbook_uri https://carddav-app.avroid.tech:6787
|
||||||
|
calendar_uri https://caldav-app.avroid.tech:6787
|
||||||
|
ldap_uri ldap://ldap-app.avroid.tech:389
|
||||||
|
grpc_uri grpc-app.avroid.tech
|
||||||
|
grpc_port 3142
|
||||||
|
grpc_ssl SSL
|
||||||
|
http_api_uri api-app.avroid.tech
|
||||||
|
http_api_port 443
|
||||||
|
http_api_ssl SSL
|
||||||
|
documentation_uri https://mail-app.avroid.tech/info
|
||||||
|
web_mail_login_page_uri https://mail-app.avroid.tech
|
||||||
|
}
|
||||||
|
dafnis {
|
||||||
|
balancer_endpoints {
|
||||||
|
hydra.ucs-infra-1.installation.example.net:50053
|
||||||
|
}
|
||||||
|
compression none
|
||||||
|
load_balanced true
|
||||||
|
request_timeout 10s
|
||||||
|
service_name dafnis
|
||||||
|
use_tls true
|
||||||
|
use_tls_balancer true
|
||||||
|
}
|
||||||
|
erakles {
|
||||||
|
balancer_endpoints {
|
||||||
|
hydra.ucs-infra-1.installation.example.net:50053
|
||||||
|
}
|
||||||
|
compression none
|
||||||
|
load_balanced true
|
||||||
|
request_timeout 10s
|
||||||
|
service_name erakles
|
||||||
|
use_tls true
|
||||||
|
use_tls_balancer true
|
||||||
|
}
|
||||||
|
minos {
|
||||||
|
balancer_endpoints {
|
||||||
|
hydra.ucs-infra-1.installation.example.net:50053
|
||||||
|
}
|
||||||
|
compression none
|
||||||
|
load_balanced true
|
||||||
|
request_timeout 10s
|
||||||
|
service_name minos
|
||||||
|
use_tls true
|
||||||
|
use_tls_balancer true
|
||||||
|
}
|
||||||
|
perseus {
|
||||||
|
balancer_endpoints {
|
||||||
|
hydra.ucs-infra-1.installation.example.net:50053
|
||||||
|
}
|
||||||
|
compression none
|
||||||
|
load_balanced true
|
||||||
|
request_timeout 10s
|
||||||
|
service_name perseus
|
||||||
|
use_tls true
|
||||||
|
use_tls_balancer true
|
||||||
|
}
|
||||||
|
tls_settings {
|
||||||
|
ca_file /etc/pki/tls/certs/ucs-infra-1.installation.example.net-main-ca.pem
|
||||||
|
client_auth_type require_and_verify_client_cert
|
||||||
|
client_cert_file /etc/pki/tls/certs/house.ucs-infra-1.installation.example.net-main-client.pem
|
||||||
|
key_file /etc/pki/tls/private/house.ucs-infra-1.installation.example.net-main-key.pem
|
||||||
|
tls_min_version tls1_2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logging
|
||||||
|
|
||||||
|
request-report
|
||||||
|
|
||||||
|
tracing
|
||||||
|
}
|
||||||
107
myoffice_projects/house_configs/default.conf
Normal file
107
myoffice_projects/house_configs/default.conf
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
https://app.avroid.tech/.well-known/caldav {
|
||||||
|
tls /etc/pki/tls/certs/bundle_server.crt /etc/pki/tls/private/server.nopass.key
|
||||||
|
|
||||||
|
|
||||||
|
redir 301 {
|
||||||
|
/ https://caldav-app.avroid.tech:6787/.well-known/caldav
|
||||||
|
}
|
||||||
|
|
||||||
|
logging
|
||||||
|
|
||||||
|
request-report
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
https://app.avroid.tech/.well-known/carddav {
|
||||||
|
tls /etc/pki/tls/certs/bundle_server.crt /etc/pki/tls/private/server.nopass.key
|
||||||
|
|
||||||
|
|
||||||
|
redir 301 {
|
||||||
|
/ https://carddav-app.avroid.tech:6787/.well-known/carddav
|
||||||
|
}
|
||||||
|
|
||||||
|
logging
|
||||||
|
|
||||||
|
request-report
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
https://app.avroid.tech, https://www.app.avroid.tech {
|
||||||
|
tls /etc/pki/tls/certs/bundle_server.crt /etc/pki/tls/private/server.nopass.key
|
||||||
|
|
||||||
|
|
||||||
|
redir 301 {
|
||||||
|
/ https://mail-app.avroid.tech{uri}
|
||||||
|
}
|
||||||
|
|
||||||
|
logging
|
||||||
|
|
||||||
|
request-report
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
http://app.avroid.tech, http://www.app.avroid.tech {
|
||||||
|
|
||||||
|
tls off
|
||||||
|
|
||||||
|
redir 301 {
|
||||||
|
/ https://mail-app.avroid.tech{uri}
|
||||||
|
}
|
||||||
|
|
||||||
|
logging
|
||||||
|
|
||||||
|
request-report
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
https://avroid.tech,
|
||||||
|
https://www.avroid.tech,
|
||||||
|
https://relay-app.avroid.tech,
|
||||||
|
https://www.relay-app.avroid.tech,
|
||||||
|
https://mx-app.avroid.tech,
|
||||||
|
https://www.mx-app.avroid.tech,
|
||||||
|
https://smtp-app.avroid.tech,
|
||||||
|
https://www.smtp-app.avroid.tech,
|
||||||
|
https://secured-app.avroid.tech,
|
||||||
|
https://www.secured-app.avroid.tech,
|
||||||
|
https://imap-app.avroid.tech,
|
||||||
|
https://www.imap-app.avroid.tech,
|
||||||
|
https://settings-app.avroid.tech,
|
||||||
|
https://www.settings-app.avroid.tech,
|
||||||
|
https://info-app.avroid.tech,
|
||||||
|
https://www.info-app.avroid.tech,
|
||||||
|
https://caldav-app.avroid.tech,
|
||||||
|
https://www.caldav-app.avroid.tech,
|
||||||
|
https://carddav-app.avroid.tech,
|
||||||
|
https://www.carddav-app.avroid.tech,
|
||||||
|
https://squadus-app.avroid.tech,
|
||||||
|
https://www.squadus-app.avroid.tech,
|
||||||
|
https://www.coappacts-app.avroid.tech,
|
||||||
|
https://coappacts-app.avroid.tech {
|
||||||
|
tls /etc/pki/tls/certs/bundle_server.crt /etc/pki/tls/private/server.nopass.key
|
||||||
|
|
||||||
|
|
||||||
|
redir 301 {
|
||||||
|
/ https://avroid.ru{uri}
|
||||||
|
}
|
||||||
|
|
||||||
|
logging
|
||||||
|
|
||||||
|
request-report
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
https://www.mail.avroid.tech,
|
||||||
|
https://mail.avroid.tech {
|
||||||
|
tls /etc/pki/tls/certs/bundle_server.crt /etc/pki/tls/private/server.nopass.key
|
||||||
|
|
||||||
|
|
||||||
|
redir 301 {
|
||||||
|
/ https://app.avroid.tech{uri}
|
||||||
|
}
|
||||||
|
|
||||||
|
logging
|
||||||
|
|
||||||
|
request-report
|
||||||
|
|
||||||
|
}
|
||||||
89
myoffice_projects/import_mailion/export_groups_from_ldap.py
Executable file
89
myoffice_projects/import_mailion/export_groups_from_ldap.py
Executable file
@@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import ldap
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
def write_to_file(filename, group_list):
|
||||||
|
pattern = r'child_\d+'
|
||||||
|
|
||||||
|
#Save group's list in JSON file
|
||||||
|
with open(filename, 'w') as file:
|
||||||
|
json.dump(group_list, file, indent=4)
|
||||||
|
|
||||||
|
with open(filename, 'r') as file:
|
||||||
|
lines = file.readlines()
|
||||||
|
|
||||||
|
lines = lines[1:-1] # delete first and last lines
|
||||||
|
|
||||||
|
# Change "}," to "}"
|
||||||
|
for i in range(len(lines)):
|
||||||
|
lines[i] = lines[i].replace('},', '}')
|
||||||
|
lines[i] = re.sub(pattern, 'child', lines[i])
|
||||||
|
|
||||||
|
with open(filename, 'w') as file:
|
||||||
|
for line in lines:
|
||||||
|
file.write(line)
|
||||||
|
|
||||||
|
# OpenLDAP server config
|
||||||
|
ldap_server = 'ldap://ds.avroid.tech'
|
||||||
|
ldap_port = 389
|
||||||
|
ldap_base_dn = 'dc=avroid,dc=tech'
|
||||||
|
ldap_admin_user = 'uid=ipa,cn=users,cn=accounts,dc=avroid,dc=tech'
|
||||||
|
ldap_admin_password = '<PASSWORD>'
|
||||||
|
|
||||||
|
# LDAP query
|
||||||
|
ldap_filter = '(objectClass=ipantgroupattrs)'
|
||||||
|
|
||||||
|
# Connect to the LDAP server
|
||||||
|
ldap_connection = ldap.initialize(ldap_server + ':' + str(ldap_port))
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Bind to the LDAP server using admin credentials
|
||||||
|
ldap_connection.simple_bind_s(ldap_admin_user, ldap_admin_password)
|
||||||
|
results = ldap_connection.search_s(ldap_base_dn, ldap.SCOPE_SUBTREE, ldap_filter)
|
||||||
|
|
||||||
|
# Prepare a lists for groups
|
||||||
|
groups = []
|
||||||
|
group_links = []
|
||||||
|
i = 0
|
||||||
|
# Extract groups information
|
||||||
|
for dn, entry in results:
|
||||||
|
group = {}
|
||||||
|
|
||||||
|
# Extract and store group attributes
|
||||||
|
if ' ' not in entry.get('cn', [None])[0].decode('utf-8'):
|
||||||
|
group['correlation_id'] = entry.get('gidNumber', [None])[0].decode('utf-8') if entry.get('gidNumber') else None
|
||||||
|
group['name'] = entry.get('cn', [None])[0].decode('utf-8') if entry.get('cn') else None
|
||||||
|
group['description'] = entry.get('description', [None])[0].decode('utf-8') if entry.get('description') else None
|
||||||
|
group['email'] = group['name'] + '@' + dn.split(',')[3].split('=')[1] + '.' + dn.split(',')[4].split('=')[1]
|
||||||
|
|
||||||
|
groups.append(group)
|
||||||
|
|
||||||
|
# Extract and store group attributes
|
||||||
|
for child in entry.get('member', [None]):
|
||||||
|
group_link = {}
|
||||||
|
if child is not None and child.decode('utf-8').split(',')[1].split('=')[1] in ['groups', 'users']:
|
||||||
|
num = str(i)
|
||||||
|
group_link['correlation_id'] = num
|
||||||
|
group_link['parent'] = group['email']
|
||||||
|
child = child.decode('utf-8')
|
||||||
|
group_link['child'] = child.split(',')[0].split('=')[1]
|
||||||
|
group_link['child'] += '@'
|
||||||
|
group_link['child'] += child.split(',')[3].split('=')[1]
|
||||||
|
group_link['child'] += '.'
|
||||||
|
group_link['child'] += child.split(',')[4].split('=')[1]
|
||||||
|
|
||||||
|
group_links.append(group_link)
|
||||||
|
i += 1
|
||||||
|
write_to_file("groups.json", groups)
|
||||||
|
write_to_file("group_links.json", group_links)
|
||||||
|
|
||||||
|
print('Successfully exported groups.')
|
||||||
|
|
||||||
|
except ldap.LDAPError as e:
|
||||||
|
print('LDAP Error:', e)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# Unbind from the LDAP server
|
||||||
|
ldap_connection.unbind()
|
||||||
27
myoffice_projects/import_mailion/import_config.json
Normal file
27
myoffice_projects/import_mailion/import_config.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"token-name": "ucs-access-token",
|
||||||
|
"admin": {
|
||||||
|
"login": "admin.myoffice",
|
||||||
|
"password": "ideapheeF2Ru3niZ"
|
||||||
|
},
|
||||||
|
"cox": {
|
||||||
|
"compression": "none",
|
||||||
|
"endpoint": "grpc-app.avroid.tech:3142",
|
||||||
|
"load_balanced": false,
|
||||||
|
"request_timeout": "10s",
|
||||||
|
"use_tls": true
|
||||||
|
},
|
||||||
|
"tls_settings": {
|
||||||
|
"ca_file": "/srv/tls/certs/ucs-infra-1.installation.example.net-main-ca.pem",
|
||||||
|
"client_cert_file": "/srv/tls/certs/ministerium.ucs-infra-1.installation.example.net-main-client.pem",
|
||||||
|
"key_file": "/srv/tls/keys/ministerium.ucs-infra-1.installation.example.net-main-key.pem"
|
||||||
|
},
|
||||||
|
"tenant_id": "c25c71b4-5f87-4d58-a38b-a504bf43585e",
|
||||||
|
"region_id": "4ba3c930-5ff9-4933-b0a9-20ff328e2fc5",
|
||||||
|
"gal_tags": [
|
||||||
|
"05ea1eea-e273-55a6-86dd-3a932860211e" ],
|
||||||
|
"user_data_path": "user_profiles.json",
|
||||||
|
"user_data_format": "json",
|
||||||
|
"rejected_users_path": "rejected_profiles.json",
|
||||||
|
"roles": []
|
||||||
|
}
|
||||||
7
myoffice_projects/import_mailion/mailion_import.sh
Executable file
7
myoffice_projects/import_mailion/mailion_import.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
pushd /root/import_mailion
|
||||||
|
./export_groups_from_ldap.py
|
||||||
|
nct_ministerium import_groups --config settings.json
|
||||||
|
nct_ministerium import_groups_links --config settings_links.json
|
||||||
|
popd
|
||||||
27
myoffice_projects/import_mailion/settings.json
Normal file
27
myoffice_projects/import_mailion/settings.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"token-name": "ucs-access-token",
|
||||||
|
"admin": {
|
||||||
|
"login": "admin.myoffice",
|
||||||
|
"password": "ideapheeF2Ru3niZ"
|
||||||
|
},
|
||||||
|
"cox": {
|
||||||
|
"endpoint": "grpc-app.avroid.tech:3142",
|
||||||
|
"service_name": "cox",
|
||||||
|
"load_balanced": false,
|
||||||
|
"use_tls": true,
|
||||||
|
"use_tls_balancer": false,
|
||||||
|
"compression": "none"
|
||||||
|
},
|
||||||
|
"tls_settings": {
|
||||||
|
"ca_file": "/srv/tls/certs/ucs-infra-1.installation.example.net-main-ca.pem",
|
||||||
|
"client_cert_file": "/srv/tls/certs/ministerium.ucs-infra-1.installation.example.net-main-client.pem",
|
||||||
|
"key_file": "/srv/tls/keys/ministerium.ucs-infra-1.installation.example.net-main-key.pem"
|
||||||
|
},
|
||||||
|
"tenant_id": "c25c71b4-5f87-4d58-a38b-a504bf43585e",
|
||||||
|
"region_id": "4ba3c930-5ff9-4933-b0a9-20ff328e2fc5",
|
||||||
|
"gal_tags": [
|
||||||
|
"05ea1eea-e273-55a6-86dd-3a932860211e" ],
|
||||||
|
"groups_data_path": "groups.json",
|
||||||
|
"groups_data_format": "json",
|
||||||
|
"rejected_groups_path": "rejected_groups.json"
|
||||||
|
}
|
||||||
27
myoffice_projects/import_mailion/settings_links.json
Normal file
27
myoffice_projects/import_mailion/settings_links.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"token-name": "ucs-access-token",
|
||||||
|
"admin": {
|
||||||
|
"login": "admin.myoffice",
|
||||||
|
"password": "ideapheeF2Ru3niZ"
|
||||||
|
},
|
||||||
|
"cox": {
|
||||||
|
"endpoint": "grpc-app.avroid.tech:3142",
|
||||||
|
"service_name": "cox",
|
||||||
|
"load_balanced": false,
|
||||||
|
"use_tls": true,
|
||||||
|
"use_tls_balancer": false,
|
||||||
|
"compression": "none"
|
||||||
|
},
|
||||||
|
"tls_settings": {
|
||||||
|
"ca_file": "/srv/tls/certs/ucs-infra-1.installation.example.net-main-ca.pem",
|
||||||
|
"client_cert_file": "/srv/tls/certs/ministerium.ucs-infra-1.installation.example.net-main-client.pem",
|
||||||
|
"key_file": "/srv/tls/keys/ministerium.ucs-infra-1.installation.example.net-main-key.pem"
|
||||||
|
},
|
||||||
|
"tenant_id": "c25c71b4-5f87-4d58-a38b-a504bf43585e",
|
||||||
|
"region_id": "4ba3c930-5ff9-4933-b0a9-20ff328e2fc5",
|
||||||
|
"gal_tags": [
|
||||||
|
"05ea1eea-e273-55a6-86dd-3a932860211e" ],
|
||||||
|
"group_links_data_path": "group_links.json",
|
||||||
|
"group_links_data_format": "json",
|
||||||
|
"rejected_groups_path": "rejected_groups.json"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user