add myoffice scripts

This commit is contained in:
aleksandr.vodyanov
2024-04-09 10:19:48 +03:00
parent 6596117571
commit 9f2c40e688
28 changed files with 1310 additions and 0 deletions

View 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
`

View 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 |

View 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}"]

View 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 .. ">.");
```

View 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"])

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,5 @@
flask
flask-restful
pip
python-freeipa
requests

View 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"]

View 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
```

View File

@@ -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 * * * *"

View 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)

View 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`

View File

@@ -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

View 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>

View File

@@ -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());
}
}
}

View File

@@ -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() {
}
}

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -0,0 +1,2 @@
# SPI class implementation
team.hyperus.PgsStorageProviderFactory

View 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
}

View 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
}

View 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()

View 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": []
}

View 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

View 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"
}

View 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"
}