From cda608b6684a7eb56b0b2d2f6bbac7bcffcaac7e Mon Sep 17 00:00:00 2001
From: Julian Rother <julian@jrother.eu>
Date: Thu, 18 Nov 2021 20:59:30 +0100
Subject: [PATCH] Debian package

---
 .gitlab-ci.yml                   |  58 ++++++++++++++++-
 debian/contrib/uffd-ldap.conf    |  10 +++
 debian/contrib/uffd-ldap.service |  43 +++++++++++++
 debian/contrib/uffd-ldap.socket  |   8 +++
 debian/control                   |  19 ++++++
 debian/create_changelog.py       | 106 +++++++++++++++++++++++++++++++
 debian/install                   |   4 ++
 debian/links                     |   1 +
 debian/postinst                  |  21 ++++++
 debian/rules                     |   3 +
 requirements.txt                 |   1 +
 server.py                        |   1 +
 12 files changed, 274 insertions(+), 1 deletion(-)
 create mode 100644 debian/contrib/uffd-ldap.conf
 create mode 100644 debian/contrib/uffd-ldap.service
 create mode 100644 debian/contrib/uffd-ldap.socket
 create mode 100644 debian/control
 create mode 100755 debian/create_changelog.py
 create mode 100644 debian/install
 create mode 100644 debian/links
 create mode 100755 debian/postinst
 create mode 100755 debian/rules
 mode change 100644 => 100755 server.py

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 18c2cfc..2ca9800 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,6 +3,8 @@ image: registry.git.cccv.de/uffd/docker-images/buster
 variables:
   DEBIAN_FRONTEND: noninteractive
   GIT_SUBMODULE_STRATEGY: normal
+  APT_API_URL: https://packages.cccv.de
+  APT_REPO: uffd
   PYLINT_PIN: pylint~=2.10.0
 
 before_script:
@@ -10,7 +12,24 @@ before_script:
   - lsb_release -a
   - uname -a
   - python3 -m pylint --version
- 
+  - python3 -m coverage --version
+  - echo "${CI_COMMIT_TAG}" | grep -qE "v[0-9]+[.][0-9]+[.][0-9]+.*" && export PACKAGE_VERSION="${CI_COMMIT_TAG#v}" || export PACKAGE_VERSION="${CI_COMMIT_SHA}"
+
+.build:
+  stage: build
+
+build:apt:
+  extends: .build
+  script:
+  - ./debian/create_changelog.py uffd-ldap > debian/changelog
+  - dpkg-buildpackage -us -uc
+  - mv ../*.deb ./
+  - dpkg-deb -I *.deb
+  - dpkg-deb -c *.deb
+  artifacts:
+    paths:
+    - ./*.deb
+
 linter:buster:
   image: registry.git.cccv.de/uffd/docker-images/buster
   stage: test
@@ -42,3 +61,40 @@ linter:bullseye:
     - pylint.html
     reports:
       codequality: codeclimate.json
+
+test:package:apt:buster:
+  image: registry.git.cccv.de/uffd/docker-images/buster
+  stage: test
+  script:
+  - apt update
+  - apt -y install ./*.deb
+  dependencies:
+  - build:apt
+
+test:package:apt:bullseye:
+  image: registry.git.cccv.de/uffd/docker-images/bullseye
+  stage: test
+  script:
+  - apt update
+  - apt -y install ./*.deb
+  dependencies:
+  - build:apt
+
+.publish:
+  stage: deploy
+  rules:
+    - if: '$CI_COMMIT_TAG =~ /v[0-9]+[.][0-9]+[.][0-9]+.*/'
+
+publish:apt:
+  extends: .publish
+  script:
+  - export DEBPATH="$(echo *.deb)"
+  - echo Upload deb file, add it to repo and clean up upload
+  - curl --user "${APTLY_API_USER}:${APTLY_API_PW}" -X POST -F file=@"$DEBPATH" "${APT_API_URL}/api/files/${APT_REPO}-ci-upload-${CI_JOB_ID}"
+  - curl --user "${APTLY_API_USER}:${APTLY_API_PW}" -X POST "${APT_API_URL}/api/repos/${APT_REPO}/file/${APT_REPO}-ci-upload-${CI_JOB_ID}"
+  - curl --user "${APTLY_API_USER}:${APTLY_API_PW}" -X DELETE "${APT_API_URL}/api/files/${APT_REPO}-ci-upload-${CI_JOB_ID}"
+  - echo Update published repo for all distros
+  - 'curl --user "${APTLY_API_USER}:${APTLY_API_PW}" -X PUT -H "Content-Type: application/json" --data "{ }" "${APT_API_URL}/api/publish/uffd/buster"'
+  - 'curl --user "${APTLY_API_USER}:${APTLY_API_PW}" -X PUT -H "Content-Type: application/json" --data "{ }" "${APT_API_URL}/api/publish/uffd/bullseye"'
+  dependencies:
+  - build:apt
diff --git a/debian/contrib/uffd-ldap.conf b/debian/contrib/uffd-ldap.conf
new file mode 100644
index 0000000..d63bc04
--- /dev/null
+++ b/debian/contrib/uffd-ldap.conf
@@ -0,0 +1,10 @@
+# Both options must be set
+#SERVER_API_URL="https://localhost"
+#SERVER_API_KEY="my_secret_api_token"
+#SERVER_BASE_DN="dc=example,dc=com"
+
+# If bind password is not set, anonymous binds have full read permissions.
+# Otherwise bind with "cn=service,ou=system,BASEDN" and this password.
+#SERVER_BIND_PASSWORD="SECRET-BIND-PASSWORD"
+
+#SERVER_CACHE_TTL="60"
diff --git a/debian/contrib/uffd-ldap.service b/debian/contrib/uffd-ldap.service
new file mode 100644
index 0000000..c905a43
--- /dev/null
+++ b/debian/contrib/uffd-ldap.service
@@ -0,0 +1,43 @@
+[Unit]
+Description=LDAP server to integrate uffd users/groups with LDAP applications
+After=network.target
+BindsTo=uffd-ldap.socket
+
+[Service]
+ExecStart=/usr/bin/uffd-ldap --socket-fd 3
+
+Restart=always
+RestartSec=10
+StandardOutput=journal
+StandardError=journal
+SyslogIdentifier=uffd-ldap
+
+DynamicUser=true
+PrivateUsers=true
+CapabilityBoundingSet=
+NoNewPrivileges=true
+RemoveIPC=true
+LockPersonality=true
+ProtectControlGroups=true
+ProtectKernelTunables=true
+ProtectKernelModules=true
+ProtectKernelLogs=true
+ProtectClock=true
+ProtectHostname=true
+ProtectProc=noaccess
+RestrictRealtime=true
+RestrictSUIDSGID=true
+RestrictNamespaces=true
+RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
+ProtectSystem=strict
+ProtectHome=true
+PrivateTmp=true
+PrivateDevices=true
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+MemoryDenyWriteExecute=true
+
+EnvironmentFile=/etc/uffd-ldap.conf
+
+[Install]
+WantedBy=default.target
diff --git a/debian/contrib/uffd-ldap.socket b/debian/contrib/uffd-ldap.socket
new file mode 100644
index 0000000..01552cf
--- /dev/null
+++ b/debian/contrib/uffd-ldap.socket
@@ -0,0 +1,8 @@
+[Unit]
+Description=LDAP server to integrate uffd users/groups with LDAP applications
+
+[Socket]
+ListenStream=127.0.0.1:389
+
+[Install]
+WantedBy=sockets.target
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..af00ba6
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,19 @@
+Source: uffd-ldap
+Section: python
+Priority: optional
+Maintainer: CCCV <it@cccv.de>
+Build-Depends:
+ debhelper-compat (= 12),
+Standards-Version: 4.5.0
+Homepage: https://git.cccv.de/uffd/ldap-proxy
+Vcs-Git: https://git.cccv.de/uffd/ldap-proxy.git
+
+Package: uffd-ldap
+Architecture: all
+Depends:
+ ${misc:Depends},
+ python3-ldapserver,
+ python3-requests,
+ python3-cachecontrol,
+ python3-click,
+Description: LDAP server to integrate uffd users/groups with LDAP applications
diff --git a/debian/create_changelog.py b/debian/create_changelog.py
new file mode 100755
index 0000000..b62f211
--- /dev/null
+++ b/debian/create_changelog.py
@@ -0,0 +1,106 @@
+#!/usr/bin/python3
+import sys
+import re
+import textwrap
+import datetime
+import email.utils
+
+import git
+
+package_name = 'UNKNOWN'
+
+alias_names = {
+	'julian': 'Julian Rother',
+	'Julian': 'Julian Rother',
+}
+
+ignore_commit_regexes = [
+	'^fixup!',
+]
+
+def print_release(tag=None, commits=tuple(), last_tag=None):
+	release_version = '0.0.0'
+	release_author = git.objects.util.Actor('None', 'undefined@example.com')
+	release_date = 0
+	release_status = 'UNRELEASED'
+	message = ''
+
+	if tag:
+		release_status = 'unstable'
+		release_version = tag.name[1:] # strip leading "v"
+		if isinstance(tag.object, git.TagObject):
+			release_author = tag.object.tagger
+			release_date = tag.object.tagged_date
+			message = tag.object.message.split('-----BEGIN PGP SIGNATURE-----')[0].strip()
+		else:
+			release_author = tag.object.committer
+			release_date = tag.object.committed_date
+	elif commits:
+		release_author = commits[0].committer
+		release_date = commits[0].committed_date
+		date = datetime.datetime.fromtimestamp(release_date).strftime('%Y%m%dT%H%M%S')
+		last_version = '0.0.0'
+		if last_tag:
+			last_version = last_tag.name[1:] # strip leading "v"
+		release_version = f'{last_version}+git{date}-{commits[0].hexsha[:8]}'
+
+	print(f'{package_name} ({release_version}) {release_status}; urgency=medium')
+	print()
+	if message:
+		print(textwrap.indent(message, '  '))
+		print()
+	commit_authors = [] # list of (key, author), sorted by first commit date
+	commit_author_emails = {} # author email -> key
+	commit_author_names = {} # author name -> key
+	commit_author_commits = {} # key -> list of commits
+	for commit in commits:
+		if any(filter(lambda pattern: re.match(pattern, commit.summary), ignore_commit_regexes)):
+			continue
+		if len(commit.parents) > 1:
+			continue # Ignore merge commits
+		author_name = alias_names.get(commit.author.name, commit.author.name)
+		key = commit_author_emails.get(commit.author.email)
+		if key is None:
+			key = commit_author_names.get(author_name)
+		if key is None:
+			key = commit.author.email
+			commit_authors.append((key, author_name))
+		commit_author_emails[commit.author.email] = key
+		commit_author_names[author_name] = key
+		commit_author_commits[key] = commit_author_commits.get(key, []) + [commit]
+	commit_authors.sort(key=lambda args: len(commit_author_commits[args[0]]))
+	for key, author_name in commit_authors:
+		print(f'  [ {author_name} ]')
+		for commit in commit_author_commits[key]:
+			lines = '\n'.join(textwrap.wrap(commit.summary, 90))
+			lines = '  * ' + textwrap.indent(lines, '    ').strip()
+			print(lines)
+		print()
+	print(f' -- {alias_names.get(release_author.name, release_author.name)} <{release_author.email}>  {email.utils.formatdate(release_date)}')
+
+if __name__ == '__main__':
+	repo = git.Repo('.')
+	package_name = sys.argv[1]
+
+	version_commits = {}
+	for tag in repo.tags:
+		if not re.fullmatch('v[0-9]+[.][0-9]+[.][0-9]+.*', tag.name):
+			continue
+		if isinstance(tag.object, git.TagObject):
+			commit_hexsha = tag.object.object.hexsha
+		else:
+			commit_hexsha = tag.object.hexsha
+		version_commits[commit_hexsha] = tag
+
+	tag = None
+	commits = []
+	for commit in repo.iter_commits('HEAD'):
+		if commit.hexsha in version_commits:
+			prev_tag = version_commits[commit.hexsha]
+			if commits:
+				print_release(tag, commits, last_tag=prev_tag)
+				print()
+			tag = prev_tag
+			commits = []
+		commits.append(commit)
+	print_release(tag, commits)
diff --git a/debian/install b/debian/install
new file mode 100644
index 0000000..554842b
--- /dev/null
+++ b/debian/install
@@ -0,0 +1,4 @@
+server.py /usr/lib/uffd-ldap/
+debian/contrib/uffd-ldap.service /usr/lib/systemd/system/
+debian/contrib/uffd-ldap.socket /usr/lib/systemd/system/
+debian/contrib/uffd-ldap.conf /etc/
diff --git a/debian/links b/debian/links
new file mode 100644
index 0000000..90e1e14
--- /dev/null
+++ b/debian/links
@@ -0,0 +1 @@
+/usr/lib/uffd-ldap/server.py /usr/bin/uffd-ldap
diff --git a/debian/postinst b/debian/postinst
new file mode 100755
index 0000000..e0121c1
--- /dev/null
+++ b/debian/postinst
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+set -e
+
+case "$1" in
+	configure)
+		chmod 0640 /etc/uffd-ldap.conf
+	;;
+
+	abort-upgrade|abort-remove|abort-deconfigure)
+	;;
+
+	*)
+		echo "postinst called with unknown argument \`$1'" >&2
+		exit 1
+	;;
+esac
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..cbe925d
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,3 @@
+#!/usr/bin/make -f
+%:
+	dh $@
diff --git a/requirements.txt b/requirements.txt
index 11f9374..9cbab77 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,3 +3,4 @@ ldapserver==0.0.1.dev6
 
 requests==2.*
 CacheControl
+click==7.*
diff --git a/server.py b/server.py
old mode 100644
new mode 100755
index a0db3f4..17d95fb
--- a/server.py
+++ b/server.py
@@ -1,3 +1,4 @@
+#!/usr/bin/python3
 import os
 import sys
 import socketserver
-- 
GitLab