Merge pull request #8640 from jumpserver/dev

v2.24.0-rc5
pull/8658/head
Jiangjie.Bai 2022-07-20 19:07:18 +08:00 committed by GitHub
commit d34c4fb7ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 115 additions and 93 deletions

View File

@ -125,6 +125,9 @@ class CommandFilterRule(OrgModelMixin):
regex.append(cmd)
continue
if not cmd:
continue
# 如果是单个字符
if cmd[-1].isalpha():
regex.append(r'\b{0}\b'.format(cmd))

View File

@ -29,7 +29,7 @@ def clean_ftp_log_period():
now = timezone.now()
days = get_log_keep_day('FTP_LOG_KEEP_DAYS')
expired_day = now - datetime.timedelta(days=days)
FTPLog.objects.filter(datetime__lt=expired_day).delete()
FTPLog.objects.filter(date_start__lt=expired_day).delete()
@register_as_period_task(interval=3600*24)

View File

@ -1,3 +1,4 @@
import abc
import os
import json
import base64
@ -16,8 +17,8 @@ from orgs.mixins.api import RootOrgViewMixin
from perms.models.base import Action
from terminal.models import EndpointRule
from ..serializers import (
ConnectionTokenSerializer, ConnectionTokenSecretSerializer, SuperConnectionTokenSerializer,
ConnectionTokenDisplaySerializer,
ConnectionTokenSerializer, ConnectionTokenSecretSerializer,
SuperConnectionTokenSerializer, ConnectionTokenDisplaySerializer,
)
from ..models import ConnectionToken
@ -34,9 +35,12 @@ class ConnectionTokenMixin:
if not is_valid:
raise PermissionDenied(error)
@staticmethod
def get_request_resources(serializer):
user = serializer.validated_data.get('user')
@abc.abstractmethod
def get_request_resource_user(self, serializer):
raise NotImplementedError
def get_request_resources(self, serializer):
user = self.get_request_resource_user(serializer)
asset = serializer.validated_data.get('asset')
application = serializer.validated_data.get('application')
system_user = serializer.validated_data.get('system_user')
@ -91,11 +95,6 @@ class ConnectionTokenMixin:
"config": rdp_config
}
def get_host(self, endpoint):
if not endpoint.host:
return self.request.get_host()
return endpoint.host
def get_rdp_file_info(self, token: ConnectionToken):
rdp_options = {
'full address:s': '',
@ -145,9 +144,7 @@ class ConnectionTokenMixin:
endpoint = self.get_smart_endpoint(
protocol='rdp', asset=token.asset, application=token.application
)
# TODO 暂时获取一下host后续优化
host = self.get_host(endpoint)
rdp_options['full address:s'] = f'{host}:{endpoint.rdp_port}'
rdp_options['full address:s'] = f'{endpoint.host}:{endpoint.rdp_port}'
# 设置用户名
rdp_options['username:s'] = '{}|{}'.format(token.user.username, str(token.id))
@ -199,10 +196,8 @@ class ConnectionTokenMixin:
endpoint = self.get_smart_endpoint(
protocol='ssh', asset=token.asset, application=token.application
)
# TODO 暂时获取一下host后续优化
host = self.get_host(endpoint)
data = {
'ip': host,
'ip': endpoint.host,
'port': str(endpoint.ssh_port),
'username': 'JMS-{}'.format(str(token.id)),
'password': token.secret
@ -213,8 +208,8 @@ class ConnectionTokenMixin:
class ConnectionTokenViewSet(ConnectionTokenMixin, RootOrgViewMixin, JMSModelViewSet):
filterset_fields = (
'type',
'user_display', 'system_user_display', 'application_display', 'asset_display'
'type', 'user_display', 'system_user_display',
'application_display', 'asset_display'
)
search_fields = filterset_fields
serializer_classes = {
@ -235,6 +230,17 @@ class ConnectionTokenViewSet(ConnectionTokenMixin, RootOrgViewMixin, JMSModelVie
def get_queryset(self):
return ConnectionToken.objects.filter(user=self.request.user)
def get_request_resource_user(self, serializer):
return self.request.user
def get_object(self):
if self.request.user.is_service_account:
# TODO: 组件获取 token 详情,将来放在 Super-connection-token API 中
obj = get_object_or_404(ConnectionToken, pk=self.kwargs.get('pk'))
else:
obj = super(ConnectionTokenViewSet, self).get_object()
return obj
def create_connection_token(self):
data = self.request.query_params if self.request.method == 'GET' else self.request.data
serializer = self.get_serializer(data=data)
@ -302,6 +308,9 @@ class SuperConnectionTokenViewSet(ConnectionTokenViewSet):
'renewal': 'authentication.add_superconnectiontoken'
}
def get_request_resource_user(self, serializer):
return serializer.validated_data.get('user')
@action(methods=['PATCH'], detail=False)
def renewal(self, request, *args, **kwargs):
from common.utils.timezone import as_current_tz

View File

@ -27,9 +27,8 @@ class ConnectionTokenSerializer(OrgResourceModelSerializerMixin):
model = ConnectionToken
fields_mini = ['id', 'type']
fields_small = fields_mini + [
'secret', 'date_expired',
'date_created', 'date_updated', 'created_by', 'updated_by',
'org_id', 'org_name',
'secret', 'date_expired', 'date_created', 'date_updated',
'created_by', 'updated_by', 'org_id', 'org_name',
]
fields_fk = [
'user', 'system_user', 'asset', 'application',
@ -37,8 +36,8 @@ class ConnectionTokenSerializer(OrgResourceModelSerializerMixin):
read_only_fields = [
# 普通 Token 不支持指定 user
'user', 'is_valid', 'expire_time',
'type_display', 'user_display', 'system_user_display', 'asset_display',
'application_display',
'type_display', 'user_display', 'system_user_display',
'asset_display', 'application_display',
]
fields = fields_small + fields_fk + read_only_fields
@ -102,8 +101,8 @@ class SuperConnectionTokenSerializer(ConnectionTokenSerializer):
class Meta(ConnectionTokenSerializer.Meta):
read_only_fields = [
'validity',
'user_display', 'system_user_display', 'asset_display', 'application_display',
'validity', 'user_display', 'system_user_display',
'asset_display', 'application_display',
]
def get_user(self, attrs):

View File

@ -11,7 +11,7 @@ default_interface = dict((
('favicon', static('img/facio.ico')),
('login_title', _('JumpServer Open Source Bastion Host')),
('theme', 'classic_green'),
('theme_info', None),
('theme_info', {}),
))
default_context = {

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-07-19 10:45+0800\n"
"POT-Creation-Date: 2022-07-20 13:51+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -29,7 +29,7 @@ msgstr "Acls"
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
#: orgs/models.py:65 perms/models/base.py:83 rbac/models/role.py:29
#: settings/models.py:29 settings/serializers/sms.py:6
#: terminal/models/endpoint.py:10 terminal/models/endpoint.py:88
#: terminal/models/endpoint.py:10 terminal/models/endpoint.py:86
#: terminal/models/storage.py:26 terminal/models/task.py:16
#: terminal/models/terminal.py:100 users/forms/profile.py:33
#: users/models/group.py:15 users/models/user.py:661
@ -38,12 +38,12 @@ msgid "Name"
msgstr "名前"
#: acls/models/base.py:27 assets/models/cmd_filter.py:84
#: assets/models/user.py:251 terminal/models/endpoint.py:91
#: assets/models/user.py:251 terminal/models/endpoint.py:89
msgid "Priority"
msgstr "優先順位"
#: acls/models/base.py:28 assets/models/cmd_filter.py:84
#: assets/models/user.py:251 terminal/models/endpoint.py:92
#: assets/models/user.py:251 terminal/models/endpoint.py:90
msgid "1-100, the lower the value will be match first"
msgstr "1-100、低い値は最初に一致します"
@ -61,7 +61,7 @@ msgstr "アクティブ"
#: assets/models/domain.py:65 assets/models/group.py:23
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:68
#: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:34
#: terminal/models/endpoint.py:23 terminal/models/endpoint.py:98
#: terminal/models/endpoint.py:23 terminal/models/endpoint.py:96
#: terminal/models/storage.py:29 terminal/models/terminal.py:114
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
#: users/models/group.py:16 users/models/user.py:698
@ -157,7 +157,7 @@ msgstr "コンマ区切り文字列の形式。* はすべて一致すること
#: acls/serializers/login_asset_acl.py:51 assets/models/base.py:176
#: assets/models/gathered_user.py:15 audits/models.py:121
#: authentication/forms.py:25 authentication/forms.py:27
#: authentication/models.py:253
#: authentication/models.py:260
#: authentication/templates/authentication/_msg_different_city.html:9
#: authentication/templates/authentication/_msg_oauth_bind.html:9
#: ops/models/adhoc.py:159 users/forms/profile.py:32 users/models/user.py:659
@ -781,7 +781,7 @@ msgstr "失敗しました"
msgid "Connectivity"
msgstr "接続性"
#: assets/models/base.py:40 authentication/models.py:256
#: assets/models/base.py:40 authentication/models.py:263
msgid "Date verified"
msgstr "確認済みの日付"
@ -1629,7 +1629,7 @@ msgstr "本を飛ばす"
msgid "DingTalk"
msgstr "DingTalk"
#: audits/signal_handlers.py:56 authentication/models.py:260
#: audits/signal_handlers.py:56 authentication/models.py:267
msgid "Temporary token"
msgstr "仮パスワード"
@ -2137,13 +2137,13 @@ msgstr "期限切れ"
msgid "SSO token"
msgstr "SSO token"
#: authentication/models.py:72 authentication/models.py:254
#: authentication/models.py:72 authentication/models.py:261
#: authentication/templates/authentication/_access_key_modal.html:31
#: settings/serializers/auth/radius.py:17
msgid "Secret"
msgstr "ひみつ"
#: authentication/models.py:74 authentication/models.py:257
#: authentication/models.py:74 authentication/models.py:264
#: perms/models/base.py:90 tickets/models/ticket/apply_application.py:26
#: tickets/models/ticket/apply_asset.py:24 users/models/user.py:703
msgid "Date expired"
@ -2201,11 +2201,11 @@ msgstr ""
"ユーザーがアプリにアクセスする権限を持っていないか、権限の有効期限が切れてい"
"ます"
#: authentication/models.py:255
#: authentication/models.py:262
msgid "Verified"
msgstr "確認済み"
#: authentication/models.py:276
#: authentication/models.py:283
msgid "Super connection token"
msgstr "スーパー接続トークン"
@ -4882,18 +4882,18 @@ msgstr "Oracle 11g ポート"
msgid "Oracle 12c Port"
msgstr "Oracle 12c ポート"
#: terminal/models/endpoint.py:28 terminal/models/endpoint.py:96
#: terminal/models/endpoint.py:28 terminal/models/endpoint.py:94
#: terminal/serializers/endpoint.py:57 terminal/serializers/storage.py:38
#: terminal/serializers/storage.py:50 terminal/serializers/storage.py:80
#: terminal/serializers/storage.py:90 terminal/serializers/storage.py:98
msgid "Endpoint"
msgstr "エンドポイント"
#: terminal/models/endpoint.py:89
#: terminal/models/endpoint.py:87
msgid "IP group"
msgstr "IP グループ"
#: terminal/models/endpoint.py:101
#: terminal/models/endpoint.py:99
msgid "Endpoint rule"
msgstr "エンドポイントルール"
@ -5050,7 +5050,7 @@ msgstr "クワーグ"
msgid "type"
msgstr "タイプ"
#: terminal/models/terminal.py:183 terminal/serializers/session.py:39
#: terminal/models/terminal.py:183
msgid "Terminal"
msgstr "ターミナル"
@ -5113,6 +5113,10 @@ msgstr "再生できます"
msgid "Can join"
msgstr "参加できます"
#: terminal/serializers/session.py:39
msgid "Terminal ID"
msgstr "ターミナル ID"
#: terminal/serializers/session.py:41
msgid "Can terminate"
msgstr "終了できます"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-07-19 10:45+0800\n"
"POT-Creation-Date: 2022-07-20 13:51+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -28,7 +28,7 @@ msgstr "访问控制"
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
#: orgs/models.py:65 perms/models/base.py:83 rbac/models/role.py:29
#: settings/models.py:29 settings/serializers/sms.py:6
#: terminal/models/endpoint.py:10 terminal/models/endpoint.py:88
#: terminal/models/endpoint.py:10 terminal/models/endpoint.py:86
#: terminal/models/storage.py:26 terminal/models/task.py:16
#: terminal/models/terminal.py:100 users/forms/profile.py:33
#: users/models/group.py:15 users/models/user.py:661
@ -37,12 +37,12 @@ msgid "Name"
msgstr "名称"
#: acls/models/base.py:27 assets/models/cmd_filter.py:84
#: assets/models/user.py:251 terminal/models/endpoint.py:91
#: assets/models/user.py:251 terminal/models/endpoint.py:89
msgid "Priority"
msgstr "优先级"
#: acls/models/base.py:28 assets/models/cmd_filter.py:84
#: assets/models/user.py:251 terminal/models/endpoint.py:92
#: assets/models/user.py:251 terminal/models/endpoint.py:90
msgid "1-100, the lower the value will be match first"
msgstr "优先级可选范围为 1-100 (数值越小越优先)"
@ -60,7 +60,7 @@ msgstr "激活中"
#: assets/models/domain.py:65 assets/models/group.py:23
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:68
#: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:34
#: terminal/models/endpoint.py:23 terminal/models/endpoint.py:98
#: terminal/models/endpoint.py:23 terminal/models/endpoint.py:96
#: terminal/models/storage.py:29 terminal/models/terminal.py:114
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
#: users/models/group.py:16 users/models/user.py:698
@ -156,7 +156,7 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
#: acls/serializers/login_asset_acl.py:51 assets/models/base.py:176
#: assets/models/gathered_user.py:15 audits/models.py:121
#: authentication/forms.py:25 authentication/forms.py:27
#: authentication/models.py:253
#: authentication/models.py:260
#: authentication/templates/authentication/_msg_different_city.html:9
#: authentication/templates/authentication/_msg_oauth_bind.html:9
#: ops/models/adhoc.py:159 users/forms/profile.py:32 users/models/user.py:659
@ -776,7 +776,7 @@ msgstr "失败"
msgid "Connectivity"
msgstr "可连接性"
#: assets/models/base.py:40 authentication/models.py:256
#: assets/models/base.py:40 authentication/models.py:263
msgid "Date verified"
msgstr "校验日期"
@ -1617,7 +1617,7 @@ msgstr "飞书"
msgid "DingTalk"
msgstr "钉钉"
#: audits/signal_handlers.py:56 authentication/models.py:260
#: audits/signal_handlers.py:56 authentication/models.py:267
msgid "Temporary token"
msgstr "临时密码"
@ -2116,13 +2116,13 @@ msgstr "过期时间"
msgid "SSO token"
msgstr "SSO token"
#: authentication/models.py:72 authentication/models.py:254
#: authentication/models.py:72 authentication/models.py:261
#: authentication/templates/authentication/_access_key_modal.html:31
#: settings/serializers/auth/radius.py:17
msgid "Secret"
msgstr "密钥"
#: authentication/models.py:74 authentication/models.py:257
#: authentication/models.py:74 authentication/models.py:264
#: perms/models/base.py:90 tickets/models/ticket/apply_application.py:26
#: tickets/models/ticket/apply_asset.py:24 users/models/user.py:703
msgid "Date expired"
@ -2176,11 +2176,11 @@ msgstr "应用不存在"
msgid "User has no permission to access application or permission expired"
msgstr "用户没有权限访问应用或权限已过期"
#: authentication/models.py:255
#: authentication/models.py:262
msgid "Verified"
msgstr "已校验"
#: authentication/models.py:276
#: authentication/models.py:283
msgid "Super connection token"
msgstr "超级连接令牌"
@ -4806,18 +4806,18 @@ msgstr "Oracle 11g 端口"
msgid "Oracle 12c Port"
msgstr "Oracle 12c 端口"
#: terminal/models/endpoint.py:28 terminal/models/endpoint.py:96
#: terminal/models/endpoint.py:28 terminal/models/endpoint.py:94
#: terminal/serializers/endpoint.py:57 terminal/serializers/storage.py:38
#: terminal/serializers/storage.py:50 terminal/serializers/storage.py:80
#: terminal/serializers/storage.py:90 terminal/serializers/storage.py:98
msgid "Endpoint"
msgstr "端点"
#: terminal/models/endpoint.py:89
#: terminal/models/endpoint.py:87
msgid "IP group"
msgstr "IP 组"
#: terminal/models/endpoint.py:101
#: terminal/models/endpoint.py:99
msgid "Endpoint rule"
msgstr "端点规则"
@ -4974,7 +4974,7 @@ msgstr "其它参数"
msgid "type"
msgstr "类型"
#: terminal/models/terminal.py:183 terminal/serializers/session.py:39
#: terminal/models/terminal.py:183
msgid "Terminal"
msgstr "终端"
@ -5035,6 +5035,10 @@ msgstr "是否可重放"
msgid "Can join"
msgstr "是否可加入"
#: terminal/serializers/session.py:39
msgid "Terminal ID"
msgstr "终端 ID"
#: terminal/serializers/session.py:41
msgid "Can terminate"
msgstr "是否可中断"

View File

@ -15,8 +15,6 @@ logger = get_logger(__file__)
class JMSBaseInventory(BaseInventory):
windows_ssh_default_shell = settings.WINDOWS_SSH_DEFAULT_SHELL
def convert_to_ansible(self, asset, run_as_admin=False):
info = {
'id': asset.id,
@ -33,7 +31,7 @@ class JMSBaseInventory(BaseInventory):
if asset.is_windows():
info["vars"].update({
"ansible_connection": "ssh",
"ansible_shell_type": self.windows_ssh_default_shell,
"ansible_shell_type": settings.WINDOWS_SSH_DEFAULT_SHELL,
})
for label in asset.labels.all():
info["vars"].update({

View File

@ -33,6 +33,8 @@ class UserAllGrantedAssetsQuerysetMixin:
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
pagination_class = AllGrantedAssetPagination
user: User
ordering_fields = ("hostname", "ip", "port", "cpu_cores")
ordering = ('hostname', )
def get_queryset(self):
if getattr(self, 'swagger_fake_view', False):

View File

@ -1,6 +1,3 @@
from urllib.parse import urljoin
from django.conf import settings
from django.utils.translation import ugettext as _
from django.template.loader import render_to_string
@ -52,7 +49,7 @@ class AssetPermsWillExpireForOrgAdminMsg(UserMessage):
url = js_reverse(
'perms:asset-permission-detail',
kwargs={'pk': perm.id}, external=True,
api_to_ui=True
api_to_ui=True, is_console=True
) + f'?oid={perm.org_id}'
items_with_url.append([perm.name, url])
return items_with_url
@ -123,9 +120,12 @@ class AppPermsWillExpireForOrgAdminMsg(UserMessage):
def get_items_with_url(self):
items_with_url = []
perm_detail_url = urljoin(settings.SITE_URL, '/ui/#/perms/app-permissions/{}')
for perm in self.perms:
url = perm_detail_url.format(perm.id) + f'?oid={perm.org_id}'
url = js_reverse(
'perms:application-permission-detail',
kwargs={'pk': perm.id}, external=True,
api_to_ui=True, is_console=True
) + f'?oid={perm.org_id}'
items_with_url.append([perm.name, url])
return items_with_url

View File

@ -18,7 +18,7 @@ from common.utils.common import lazyproperty
from common.utils import get_logger
from common.utils.timezone import local_now_date_display, utc_now
from common.exceptions import JMSException
from .models import AbstractSessionCommand
from terminal.models import Command
logger = get_logger(__file__)
@ -181,7 +181,7 @@ class CommandStore(object):
item['_source'].update({'id': item['_id']})
source_data.append(item['_source'])
return AbstractSessionCommand.from_multi_dict(source_data)
return Command.from_multi_dict(source_data)
def count(self, **query):
body = self.get_query_body(**query)

View File

@ -47,21 +47,6 @@ class AbstractSessionCommand(OrgModelMixin):
risk_mapper = dict(cls.RISK_LEVEL_CHOICES)
return risk_mapper.get(risk_level)
@classmethod
def from_dict(cls, d):
self = cls()
for k, v in d.items():
setattr(self, k, v)
return self
@classmethod
def from_multi_dict(cls, l):
commands = []
for d in l:
command = cls.from_dict(d)
commands.append(command)
return commands
def to_dict(self):
d = {}
for field in self._meta.fields:

View File

@ -1,7 +1,5 @@
from __future__ import unicode_literals
import time
from django.db import models
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _
@ -47,6 +45,21 @@ class Command(AbstractSessionCommand):
cls.objects.bulk_create(commands)
print(f'Create {len(commands)} commands of org ({org})')
@classmethod
def from_dict(cls, d):
self = cls()
for k, v in d.items():
setattr(self, k, v)
return self
@classmethod
def from_multi_dict(cls, l):
commands = []
for d in l:
command = cls.from_dict(d)
commands.append(command)
return commands
class Meta:
db_table = "terminal_command"
ordering = ('-timestamp',)

View File

@ -63,8 +63,6 @@ class Endpoint(JMSModel):
'http_port': 0,
}
endpoint, created = cls.objects.get_or_create(id=cls.default_id, defaults=data)
if not endpoint.host and request:
endpoint.host = request.get_host().split(':')[0]
return endpoint
@classmethod
@ -122,4 +120,7 @@ class EndpointRule(JMSModel):
endpoint = endpoint_rule.endpoint
else:
endpoint = Endpoint.get_or_create_default(request)
if not endpoint.host and request:
# 动态添加 current request host
endpoint.host = request.get_host().split(':')[0]
return endpoint

View File

@ -36,7 +36,7 @@ class SessionSerializer(BulkOrgResourceModelSerializer):
'is_success': {'label': _('Is success')},
'can_replay': {'label': _('Can replay')},
'can_join': {'label': _('Can join')},
'terminal': {'label': _('Terminal')},
'terminal': {'label': _('Terminal ID')},
'is_finished': {'label': _('Is finished')},
'can_terminate': {'label': _('Can terminate')},
'terminal_display': {'label': _('Terminal display')},

View File

@ -62,7 +62,7 @@ jsonfield2==4.0.0.post0
geoip2==4.5.0
ipip-ipdb==1.6.1
# Django environment
Django==3.1.14
Django==3.2.14
django-bootstrap3==14.2.0
django-filter==2.4.0
django-formtools==2.2
@ -117,6 +117,9 @@ tencentcloud-sdk-python==3.0.662
aliyun-python-sdk-core-v3==2.9.1
aliyun-python-sdk-ecs==4.10.1
huaweicloud-sdk-python==1.0.21
# python-keystoneclient need keystoneauth1>=3.4.0
# huaweicloud-sdk-python need keystoneauth1<=3.4.0
keystoneauth1==3.4.0
boto3==1.24.12
botocore==1.27.12
s3transfer==0.6.0

View File

@ -1,4 +1,5 @@
#!/bin/bash
yum -y install \
gcc-c++ sshpass mariadb-devel openldap-devel libxml2-devel \
xmlsec1-devel xmlsec1-openssl-devel libtool-ltdl-devel
xmlsec1-devel xmlsec1-openssl-devel libtool-ltdl-devel \
postgresql-devel