diff --git a/.gitignore b/.gitignore index 7c1f5a3..e7a478d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ config/config.ini -config/keys-sync -config/keys-sync.pub +config/cert-sync +config/cert-sync.pub extensions/*.php diff --git a/NOTICE b/NOTICE index 96a4d2e..4d99f4f 100644 --- a/NOTICE +++ b/NOTICE @@ -17,7 +17,7 @@ Copyright 2019 Marc Mettke THIRD PARTY ACKNOWLEDGEMENTS -Component: Original SSH Key Authority +Component: Derived from on SSH Key Authority Copyright 2013-2017 Opera Software AS diff --git a/README.md b/README.md index 50f0f80..1f42c05 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -> This is a maintained fork of [operasoftware/ssh-key-authority](https://github.com/operasoftware/ssh-key-authority). There should be no problem upgrading to this version, but it is not possible to migrate back to the old. Before migrating remember to make backups! - -SKA - SSH Key Authority +SCA - SSL Cert Authority ======================= A tool for managing user and server SSH access to any number of servers. @@ -23,7 +21,7 @@ Features Demo ---- -You can view the SSH Key Authority in action on the [demonstration server](https://ska.xiven.com/). +You can view the SSL Cert Authority in action on the [demonstration server](https://sca.xiven.com/). Use one of the following sets of username / password credentials to log in: @@ -50,15 +48,15 @@ Installation 2. Add the following directives to your Apache configuration (eg. virtual host config): - DocumentRoot /path/to/ska/public_html + DocumentRoot /path/to/sca/public_html DirectoryIndex init.php FallbackResource /init.php 3. Create a MySQL user and database (run in MySQL shell): - CREATE USER 'ska-user'@'localhost' IDENTIFIED BY 'password'; - CREATE DATABASE `ska-db` DEFAULT CHARACTER SET utf8mb4; - GRANT ALL ON `ska-db`.* to 'ska-user'@'localhost'; + CREATE USER 'sca-user'@'localhost' IDENTIFIED BY 'password'; + CREATE DATABASE `sca-db` DEFAULT CHARACTER SET utf8mb4; + GRANT ALL ON `sca-db`.* to 'sca-user'@'localhost'; 4. Copy the file `config/config-sample.ini` to `config/config.ini` and edit the settings as required. @@ -66,54 +64,54 @@ Installation 6. Set `scripts/cron.php` to run on a regular cron job. -7. Generate an SSH key pair to synchronize with. SSH Key Authority will expect to find the files as `config/keys-sync` and `config/keys-sync.pub` for the private and public keys respectively. +7. Generate an SSH key pair to synchronize with. SSL Cert Authority will expect to find the files as `config/cert-sync` and `config/cert-sync.pub` for the private and public keys respectively. 8. Install the SSH key synchronization daemon. * For systemd: - 1. Copy `services/systemd/keys-sync.service` to `/etc/systemd/system/` - 2. Modify `ExecStart` path and `User` as necessary. If SSH Key Authority is installed under `/home`, disable `ProtectHome`. + 1. Copy `services/systemd/cert-sync.service` to `/etc/systemd/system/` + 2. Modify `ExecStart` path and `User` as necessary. If SSL Cert Authority is installed under `/home`, disable `ProtectHome`. 3. `systemctl daemon-reload` - 4. `systemctl enable keys-sync.service` + 4. `systemctl enable cert-sync.service` * For sysv-init: - 1. Copy `services/init.d/keys-sync` to `/etc/init.d/` + 1. Copy `services/init.d/cert-sync` to `/etc/init.d/` 2. Modify `SCRIPT` path and `USER` as necessary. - 3. `update-rc.d keys-sync defaults` + 3. `update-rc.d cert-sync defaults` * Manual: - 1. Make sure that `scripts/syncd.php --user keys-sync` is executed whenever the system is restarted + 1. Make sure that `scripts/syncd.php --user cert-sync` is executed whenever the system is restarted Usage ----- If LDAP is enabed anyone in the LDAP group defined under `admin_group_cn` in `config/config.ini` will be able to manage accounts and servers. -Without LDAP, only the `keys-sync` users will be available after installation. With that user, it is possible to add new administrators or normal users. +Without LDAP, only the `cert-sync` users will be available after installation. With that user, it is possible to add new administrators or normal users. Key distribution ---------------- -SSH Key Authority distributes authorized keys to your servers via SSH. It does this by: +SSL Cert Authority distributes authorized keys to your servers via SSH. It does this by: -1. Connecting to the server with SSH, authorizing as the `keys-sync` user. -2. Writing the appropriate authorized keys to named user files in `/var/local/keys-sync/` (eg. all authorized keys for the root user will be written to `/var/local/keys-sync/root`). +1. Connecting to the server with SSH, authorizing as the `cert-sync` user. +2. Writing the appropriate authorized keys to named user files in `/var/local/cert-sync/` (eg. all authorized keys for the root user will be written to `/var/local/cert-sync/root`). -This means that your SSH installation will need to be reconfigured to read authorized keys from `/var/local/keys-sync/`. +This means that your SSH installation will need to be reconfigured to read authorized keys from `/var/local/cert-sync/`. Please note that doing so will deny access to any existing SSH public key authorized in the default `~/.ssh` directories. Under OpenSSH, the configuration changes needed are: - AuthorizedKeysFile /var/local/keys-sync/%u + AuthorizedKeysFile /var/local/cert-sync/%u StrictModes no -StrictModes must be disabled because the files will all be owned by the keys-sync user. +StrictModes must be disabled because the files will all be owned by the cert-sync user. -The file `/var/local/keys-sync/keys-sync` must exist, with the same contents as the `config/keys-sync.pub` file in order for the synchronization daemon to authenticate. +The file `/var/local/cert-sync/cert-sync` must exist, with the same contents as the `config/cert-sync.pub` file in order for the synchronization daemon to authenticate. Screenshots ----------- diff --git a/config/config-sample.ini b/config/config-sample.ini index 14ddfbd..a30cb61 100644 --- a/config/config-sample.ini +++ b/config/config-sample.ini @@ -1,7 +1,7 @@ -; SSH Key Authority config file +; SCA Key Authority config file [web] enabled = 1 -baseurl = https://ska.example.com +baseurl = https://sca.example.com logo = /logo-header-itmettke.png ; footer may contain HTML. Literal & " < and > should be escaped as & ; " < $gt; @@ -14,23 +14,17 @@ timeout_util = GNU coreutils ; used on e.g. alpine ; timeout_util = BusyBox -key_expiration_enabled = 0 -key_expiration_days = 180 - -minimum_rsa_key_size = 4096 -minimum_ecdsa_key_size = 384 - [security] -; It is important that SKA is able to verify that it has connected to the +; It is important that SCA is able to verify that it has connected to the ; server that it expected to connect to (otherwise it could be tricked into ; syncing the wrong keys to a server). The simplest way to accomplish this is ; through SSH host key verification. Setting either of the 2 options below to ; '0' can weaken the protection that SSH host key verification provides. -; Determine who can reset a server's SSH host key in SKA: +; Determine who can reset a server's SSH host key in SCA: ; 0: Allow server admins to reset the SSH host key for servers that they ; administer -; 1: Full SKA admin access is required to reset a server's host key +; 1: Full SCA admin access is required to reset a server's host key host_key_reset_restriction = 1 ; Determine what happens if multiple servers have the same SSH host key: @@ -47,35 +41,25 @@ host_key_collision_protection = 1 ; Determine how hostname verification is performed: ; 0: Do not perform hostname verification ; 1: Compare with the result of `hostname -f` -; 2: Compare with /var/local/keys-sync/.hostnames, fall back to `hostname -f` +; 2: Compare with /var/local/cert-sync/.hostnames, fall back to `hostname -f` ; if the file does not exist -; 3: Compare with /var/local/keys-sync/.hostnames, abort sync if the file +; 3: Compare with /var/local/cert-sync/.hostnames, abort sync if the file ; does not exist ; The last option provides the most solid verification, as a server will only ; be synced to if it has been explicitly allowed on the server itself. hostname_verification = 0 -[defaults] -; This setting will cause new servers to always have a managed account called -; "root" and for that account to be automatically added into the -; "root-accounts" group: -; -; account_groups[root] = "root-accounts" -; -; Any number of these can be specified -account_groups[root] = "accounts-root" - [email] enabled = 1 ; The mail address that outgoing mails will be sent from -from_address = ska@example.com -from_name = "SSH Key Authority system" +from_address = sca@example.com +from_name = "SCA Key Authority system" ; Where to mail security notifications to report_address = reports@example.com -report_name = "SSH Key Authority reports" +report_name = "SCA Key Authority reports" ; Where users should contact for help admin_address = admin@example.com -admin_name = "SSH Key Authority administrators" +admin_name = "SCA Key Authority administrators" ; You can use the reroute directive to redirect all outgoing mail to a single ; mail address - typically for temporary testing purposes ;reroute = test@example.com @@ -84,9 +68,9 @@ admin_name = "SSH Key Authority administrators" ; Connection details to the MySQL database hostname = localhost port = 3306 -username = ska-user +username = sca-user password = password -database = ska-db +database = sca-db [ldap] enabled = 0 @@ -114,48 +98,16 @@ user_name = cn user_email = mail ;user_superior = superioremployee -; If inactive users exist in your LDAP directory, filter with the following -; settings: -; Field to filter on: -;user_active = organizationalstatus -; Use *one* of user_active_true or user_active_false -; user_active_true means user is active if the user_active field equals its -; value -;user_active_true = 'current' -; user_active_false means user is active if the user_active field does not -; equal its value -;user_active_false = 'former' - -; Group membership attributes. Examples below are for typical setups: -; -; POSIX groups -; group_member = memberUid -; group_member_value = uid -; -; Group-of-names groups -; group_member = member -; group_member_value = dn -; -; Attribute of group where members are stored -group_member = memberUid -; User attribute to compare with -group_member_value = uid - -; Members of admin_group are given full admin access to SSH Key Authority web -; interface -admin_group_cn = ska-administrators -; By default only the admin_group_cn will be synced. This option enabled -; synchronisation of every group a user is in -full_group_sync = 0 +filter = sca-administrators [inventory] -; SSH Key Authority will read the contents of the file /etc/uuid (if it +; SCA Key Authority will read the contents of the file /etc/uuid (if it ; exists) when syncing with a server. If a value is found, it can be used as a ; link to an inventory system. ; %s in the url directive will be replaced with the value found in /etc/uuid ;url = "https://inventory.example.com/device/%s" [gpg] -; SSH Key Authority can GPG sign outgoing emails sent from the +; SCA Key Authority can GPG sign outgoing emails sent from the ; email.from_address. To do this it needs to know an appropriate key ID to use ;key_id = 0123456789ABCDEF0123456789ABCDEF01234567 diff --git a/core.php b/core.php index d8239c8..30f8e68 100644 --- a/core.php +++ b/core.php @@ -49,7 +49,7 @@ function autoload_model($classname) { // Setup database connection and models function setup_database() { - global $config, $database, $driver, $pubkey_dir, $user_dir, $group_dir, $server_dir, $server_account_dir, $event_dir, $sync_request_dir; + global $config, $database, $driver, $user_dir, $server_dir, $event_dir, $sync_request_dir; try { $database = new mysqli($config['database']['hostname'], $config['database']['username'], $config['database']['password'], $config['database']['database'], $config['database']['port']); } catch(ErrorException $e) { @@ -59,11 +59,8 @@ function setup_database() { $driver = new mysqli_driver(); $driver->report_mode = MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT; $migration_dir = new MigrationDirectory; - $pubkey_dir = new PublicKeyDirectory; $user_dir = new UserDirectory; - $group_dir = new GroupDirectory; $server_dir = new ServerDirectory; - $server_account_dir = new ServerAccountDirectory; $event_dir = new EventDirectory; $sync_request_dir = new SyncRequestDirectory; } diff --git a/docker/Dockerfile b/docker/Dockerfile index 24591c1..17c0b0f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,13 +1,13 @@ FROM alpine LABEL maintainer="Marc Mettke " -ENV SYSTEM https://github.com/mettke/ssh-key-authority.git +ENV SYSTEM https://github.com/mettke/ssl-cert-authority.git ADD entrypoint.sh /entrypoint.sh ADD healthcheck.sh /healthcheck.sh ADD cron /var/spool/cron/crontabs/root -RUN mkdir -p /var/log/keys/ /run/php/ /ska/ && \ - adduser --system --disabled-password keys-sync && \ +RUN mkdir -p /var/log/keys/ /run/php/ /sca/ && \ + adduser --system --disabled-password cert-sync && \ apk add openssh \ php7 \ php7-fpm \ @@ -25,13 +25,13 @@ RUN mkdir -p /var/log/keys/ /run/php/ /ska/ && \ echo "" >> /etc/php7/php-fpm.conf && \ chmod +x /entrypoint.sh /healthcheck.sh && \ ln -sf /dev/stderr /var/log/php7/error.log -RUN apk add git && \ - git clone ${SYSTEM} /ska && \ - apk del git && \ - chown -R keys-sync:nogroup /ska/config +# RUN apk add git && \ +# git clone ${SYSTEM} /sca && \ +# apk del git && \ +# chown -R cert-sync:nogroup /sca/config EXPOSE 9000 -VOLUME /ska/config +VOLUME /sca/config VOLUME /public_html ENTRYPOINT "/entrypoint.sh" diff --git a/docker/cron b/docker/cron index b802f71..05213c0 100644 --- a/docker/cron +++ b/docker/cron @@ -1,2 +1 @@ -0 1 * * * /ska/scripts/cron.php -*/1 * * * * /bin/ash -c "PID=$(cat /var/run/keys-sync.pid) && [ -n ${PID} -a -d /proc/${PID} ] || /ska/scripts/syncd.php --user keys-sync" +*/1 * * * * /bin/ash -c "PID=$(cat /var/run/cert-sync.pid) && [ -n ${PID} -a -d /proc/${PID} ] || /sca/scripts/syncd.php --user cert-sync" diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index a445e99..b49fa3d 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -1,42 +1,42 @@ #!/usr/bin/env ash -if [ `whoami` == 'keys-sync' ]; then - if [ ! -r /ska/config/config.ini ]; then +if [ `whoami` == 'cert-sync' ]; then + if [ ! -r /sca/config/config.ini ]; then echo "config.ini not found or incorrect permissions." - echo "Permissions must be $(id -u keys-sync):$(id -g keys-sync) with at least 400" + echo "Permissions must be $(id -u cert-sync):$(id -g cert-sync) with at least 400" exit 1 fi - if [ ! -r /ska/config/keys-sync ]; then + if [ ! -r /sca/config/cert-sync ]; then echo "private key not found or incorrect permissions." - echo "Permissions must be $(id -u keys-sync):$(id -g keys-sync) with 400" + echo "Permissions must be $(id -u cert-sync):$(id -g cert-sync) with 400" exit 1 fi - if [ ! -r /ska/config/keys-sync.pub ]; then + if [ ! -r /sca/config/cert-sync.pub ]; then echo "public key not found or incorrect permissions." - echo "Permissions must be $(id -u keys-sync):$(id -g keys-sync) with at least 400" + echo "Permissions must be $(id -u cert-sync):$(id -g cert-sync) with at least 400" exit 1 fi - if ! grep "^timeout_util = BusyBox$" /ska/config/config.ini > /dev/null; then + if ! grep "^timeout_util = BusyBox$" /sca/config/config.ini > /dev/null; then echo "timeout_util must be set to BusyBox." echo "Change it to: timeout_util = BusyBox" exit 1 fi elif [ $(id -u) = 0 ]; then - if ! sudo -u keys-sync /entrypoint.sh; then + if ! sudo -u cert-sync /entrypoint.sh; then exit 1 fi - rsync -a --delete /ska/public_html/ /public_html/ + rsync -a --delete /sca/public_html/ /public_html/ echo "Waiting for database..." for i in $(seq 1 10); do - if /ska/scripts/apply_migrations.php; then + if /sca/scripts/apply_migrations.php; then echo "Success" break fi echo "Trying again in 1 sec" sleep 1 done - + /usr/sbin/crond - /ska/scripts/syncd.php --user keys-sync + /sca/scripts/syncd.php --user cert-sync /usr/sbin/php-fpm7 -F else echo "Must be executed with root" diff --git a/docker/healthcheck.sh b/docker/healthcheck.sh index 8b38832..acbd0d6 100644 --- a/docker/healthcheck.sh +++ b/docker/healthcheck.sh @@ -1,5 +1,5 @@ #!/usr/bin/env ash -for PID_FILE in /var/run/crond.pid /var/run/keys-sync.pid /var/run/php-fpm.pid; do +for PID_FILE in /var/run/crond.pid /var/run/cert-sync.pid /var/run/php-fpm.pid; do PID=$(cat ${PID_FILE}) if ! [ -n "${PID}" -a -d "/proc/${PID}" ]; then exit 1 diff --git a/examples/httpd-ldap/README.md b/examples/httpd-ldap/README.md index 22533ce..4cca5b0 100644 --- a/examples/httpd-ldap/README.md +++ b/examples/httpd-ldap/README.md @@ -1,6 +1,6 @@ # Example: httpd + ldap -This Example shows how to use ska with httpd and ldap using docker. +This Example shows how to use sca with httpd and ldap using docker. ## Prepare setup @@ -15,11 +15,11 @@ This Example shows how to use ska with httpd and ldap using docker. If something goes wrong, check the log using: ``` -docker logs -f httpd-ldap_ska_1 +docker logs -f httpd-ldap_sca_1 ``` -## Using ska +## Using sca 1. Login using the admin account `rainbow`. 1. Add the server `test.example.com` at http://localhost/servers#add -1. Ska should be able to connet to the system and update its authorized_keys file. You can verify this by checking whether there is an `Synced successfully` next to the server. +1. Sca should be able to connet to the system and update its authorized_keys file. You can verify this by checking whether there is an `Synced successfully` next to the server. diff --git a/examples/httpd-ldap/docker-compose.yml b/examples/httpd-ldap/docker-compose.yml index 3813618..60d17b9 100644 --- a/examples/httpd-ldap/docker-compose.yml +++ b/examples/httpd-ldap/docker-compose.yml @@ -2,14 +2,14 @@ version: '2.2' services: test: image: alpine:3.8 - command: /bin/ash -c "(id keys-sync || adduser -h /var/local/keys-sync -S -D -s /bin/sh keys-sync) && chmod 711 /var/local/keys-sync && cp /key /var/local/keys-sync/keys-sync && chown keys-sync:nogroup /var/local/keys-sync/keys-sync && chmod 644 /var/local/keys-sync/keys-sync && apk add openssh && ssh-keygen -A && sed -i -e '/#StrictModes/ s/.*/StrictModes no/' /etc/ssh/sshd_config && sed -i -e '/AuthorizedKeysFile/ s/.*/AuthorizedKeysFile \/var\/local\/keys-sync\/%u/' /etc/ssh/sshd_config && passwd keys-sync -d test && passwd root -d test && /usr/sbin/sshd -D" + command: /bin/ash -c "(id cert-sync || adduser -h /var/local/cert-sync -S -D -s /bin/sh cert-sync) && chmod 711 /var/local/cert-sync && cp /key /var/local/cert-sync/cert-sync && chown cert-sync:nogroup /var/local/cert-sync/cert-sync && chmod 644 /var/local/cert-sync/cert-sync && apk add openssh && ssh-keygen -A && sed -i -e '/#StrictModes/ s/.*/StrictModes no/' /etc/ssh/sshd_config && sed -i -e '/AuthorizedKeysFile/ s/.*/AuthorizedKeysFile \/var\/local\/cert-sync\/%u/' /etc/ssh/sshd_config && passwd cert-sync -d test && passwd root -d test && /usr/sbin/sshd -D" restart: always expose: - "22" depends_on: - - ska-php + - sca-php volumes: - - ../shared/config-ldap/keys-sync.pub:/key:ro + - ../shared/config-ldap/cert-sync.pub:/key:ro networks: net: aliases: @@ -19,49 +19,49 @@ services: image: mwader/postfix-relay restart: always environment: - - POSTFIX_myhostname=ska.example.de + - POSTFIX_myhostname=sca.example.de - POSTFIX_mynetworks=0.0.0.0/0 expose: - "25" networks: - net - ska-db: + sca-db: image: mariadb restart: always environment: - MYSQL_ROOT_PASSWORD=root-password - - MYSQL_DATABASE=ska-db - - MYSQL_USER=ska-user + - MYSQL_DATABASE=sca-db + - MYSQL_USER=sca-user - MYSQL_PASSWORD=password volumes: - ./db:/var/lib/mysql:rw networks: - net - ska-php: + sca-php: build: context: ../../docker restart: always depends_on: - - ska-db + - sca-db - mail volumes: - - ../shared/config-ldap/:/ska/config/:rw + - ../shared/config-ldap/:/sca/config/:rw - ../shared/ssmtp.conf:/etc/ssmtp/ssmtp.conf:ro - ./public_html:/public_html:rw networks: - net - ska: + sca: image: httpd:alpine restart: always ports: - "80:80" depends_on: - - ska-php + - sca-php volumes: - - ./public_html:/ska/public_html:ro + - ./public_html:/sca/public_html:ro - ./httpd.conf:/usr/local/apache2/conf/httpd.conf:ro networks: - net diff --git a/examples/httpd-ldap/httpd.conf b/examples/httpd-ldap/httpd.conf index 92fdfca..c20f669 100644 --- a/examples/httpd-ldap/httpd.conf +++ b/examples/httpd-ldap/httpd.conf @@ -86,7 +86,7 @@ LoadModule watchdog_module modules/mod_watchdog.so - ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://ska-php:9000/ska/public_html/$1 + ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://sca-php:9000/sca/public_html/$1 SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 @@ -102,9 +102,9 @@ LoadModule watchdog_module modules/mod_watchdog.so Require all granted - + AuthType Basic - AuthName "SSH Key Authority" + AuthName "SCA Key Authority" AuthBasicProvider ldap AuthLDAPBindDN "uid=rainbow,ou=users,dc=test,dc=itmettke,dc=de" @@ -127,7 +127,7 @@ LoadModule watchdog_module modules/mod_watchdog.so Listen 80 ServerAdmin admin@example.com ServerRoot "/usr/local/apache2" -DocumentRoot "/ska/public_html" +DocumentRoot "/sca/public_html" ErrorLog /proc/self/fd/2 LogLevel warn diff --git a/examples/httpd-local/README.md b/examples/httpd-local/README.md index 862b7c8..8df1500 100644 --- a/examples/httpd-local/README.md +++ b/examples/httpd-local/README.md @@ -1,34 +1,34 @@ # Example: httpd + htpasswd -This Example shows how to use ska with httpd and ldap using docker. +This Example shows how to use sca with httpd and ldap using docker. ## Prepare setup 1. Start system using `docker-compose up -d` 1. Visit http://localhost -1. Login using one of the following credentials (Only keys-sync account exists at first): +1. Login using one of the following credentials (Only cert-sync account exists at first): |Username|Password|Type| |---|---|---| -|keys-sync|password|admin| +|cert-sync|password|admin| |rainbow|password|admin| |proceme|password|user| If something goes wrong, check the log using: ``` -docker logs -f httpd-local_ska_1 +docker logs -f httpd-local_sca_1 ``` -## Using ska +## Using sca -_The `keys-sync` user should only be used for the first setup. Afterwards its best to create a dedicated account per user._ +_The `cert-sync` user should only be used for the first setup. Afterwards its best to create a dedicated account per user._ -1. Login using the admin account `keys-sync`. +1. Login using the admin account `cert-sync`. 1. Create user `rainbow` as admin and user `proceme` as user at http://localhost/users#add 1. Add the server `test.example.com` at http://localhost/servers#add -1. Ska should be able to connet to the system and update its authorized_keys file. You can verify this by checking whether there is an `Synced successfully` next to the server. +1. Sca should be able to connet to the system and update its authorized_keys file. You can verify this by checking whether there is an `Synced successfully` next to the server. ## Add/Change passwords for users -1. Either install `htpasswd` on your system or connect to the httpd container using `docker exec -it httpd-local_ska_1 /bin/ash`. +1. Either install `htpasswd` on your system or connect to the httpd container using `docker exec -it httpd-local_sca_1 /bin/ash`. 1. Run `htpasswd` on the htpasswd file. Inside the container it is `htpasswd /allowed_users ` diff --git a/examples/httpd-local/docker-compose.yml b/examples/httpd-local/docker-compose.yml index b585ae8..f3c9dfe 100644 --- a/examples/httpd-local/docker-compose.yml +++ b/examples/httpd-local/docker-compose.yml @@ -2,14 +2,14 @@ version: '2.2' services: test: image: alpine:3.8 - command: /bin/ash -c "(id keys-sync || adduser -h /var/local/keys-sync -S -D -s /bin/sh keys-sync) && chmod 711 /var/local/keys-sync && cp /key /var/local/keys-sync/keys-sync && chown keys-sync:nogroup /var/local/keys-sync/keys-sync && chmod 644 /var/local/keys-sync/keys-sync && apk add openssh && ssh-keygen -A && sed -i -e '/#StrictModes/ s/.*/StrictModes no/' /etc/ssh/sshd_config && sed -i -e '/AuthorizedKeysFile/ s/.*/AuthorizedKeysFile \/var\/local\/keys-sync\/%u/' /etc/ssh/sshd_config && passwd keys-sync -d test && passwd root -d test && /usr/sbin/sshd -D" + command: /bin/ash -c "(id cert-sync || adduser -h /var/local/cert-sync -S -D -s /bin/sh cert-sync) && chmod 711 /var/local/cert-sync && cp /key /var/local/cert-sync/cert-sync && chown cert-sync:nogroup /var/local/cert-sync/cert-sync && chmod 644 /var/local/cert-sync/cert-sync && apk add openssh && ssh-keygen -A && sed -i -e '/#StrictModes/ s/.*/StrictModes no/' /etc/ssh/sshd_config && sed -i -e '/AuthorizedKeysFile/ s/.*/AuthorizedKeysFile \/var\/local\/cert-sync\/%u/' /etc/ssh/sshd_config && passwd cert-sync -d test && passwd root -d test && /usr/sbin/sshd -D" restart: always expose: - "22" depends_on: - - ska-php + - sca-php volumes: - - ../shared/config-local/keys-sync.pub:/key:ro + - ../shared/config-local/cert-sync.pub:/key:ro networks: net: aliases: @@ -19,49 +19,49 @@ services: image: mwader/postfix-relay restart: always environment: - - POSTFIX_myhostname=ska.example.de + - POSTFIX_myhostname=sca.example.de - POSTFIX_mynetworks=0.0.0.0/0 expose: - "25" networks: - net - ska-db: + sca-db: image: mariadb restart: always environment: - MYSQL_ROOT_PASSWORD=root-password - - MYSQL_DATABASE=ska-db - - MYSQL_USER=ska-user + - MYSQL_DATABASE=sca-db + - MYSQL_USER=sca-user - MYSQL_PASSWORD=password volumes: - ./db:/var/lib/mysql:rw networks: - net - ska-php: + sca-php: build: context: ../../docker restart: always depends_on: - - ska-db + - sca-db - mail volumes: - - ../shared/config-local/:/ska/config/:rw + - ../shared/config-local/:/sca/config/:rw - ../shared/ssmtp.conf:/etc/ssmtp/ssmtp.conf:ro - ./public_html:/public_html:rw networks: - net - ska: + sca: image: httpd:alpine restart: always ports: - "80:80" depends_on: - - ska-php + - sca-php volumes: - - ./public_html:/ska/public_html:ro + - ./public_html:/sca/public_html:ro - ./httpd.conf:/usr/local/apache2/conf/httpd.conf:ro - ./htpasswd.conf:/allowed_users:rw networks: diff --git a/examples/httpd-local/htpasswd.conf b/examples/httpd-local/htpasswd.conf index 1e9bc10..c8e4d97 100644 --- a/examples/httpd-local/htpasswd.conf +++ b/examples/httpd-local/htpasswd.conf @@ -1,4 +1,4 @@ -keys-sync:$apr1$PqjgspFz$DBUOsj/1yVzIf3v9Kv7wJ0 +cert-sync:$apr1$PqjgspFz$DBUOsj/1yVzIf3v9Kv7wJ0 rainbow:$apr1$uBkkgFwm$zrLgjA/5R8V1P0F/XHKUV0 proceme:$apr1$SWQKwjuM$D0FWOsBAD4lTdcYrtbZ0j0 speecif:$apr1$MCOzQE15$K4uameMTLTf3fJDSj/XfE. diff --git a/examples/httpd-local/httpd.conf b/examples/httpd-local/httpd.conf index 04c3f74..d068158 100644 --- a/examples/httpd-local/httpd.conf +++ b/examples/httpd-local/httpd.conf @@ -86,7 +86,7 @@ LoadModule watchdog_module modules/mod_watchdog.so - ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://ska-php:9000/ska/public_html/$1 + ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://sca-php:9000/sca/public_html/$1 SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 @@ -102,9 +102,9 @@ LoadModule watchdog_module modules/mod_watchdog.so Require all granted - + AuthType Basic - AuthName "SSH Key Authority" + AuthName "SSL Cert Authority" AuthBasicProvider file AuthUserFile /allowed_users @@ -124,7 +124,7 @@ LoadModule watchdog_module modules/mod_watchdog.so Listen 80 ServerAdmin admin@example.com ServerRoot "/usr/local/apache2" -DocumentRoot "/ska/public_html" +DocumentRoot "/sca/public_html" ErrorLog /proc/self/fd/2 LogLevel warn diff --git a/examples/nginx-local/README.md b/examples/nginx-local/README.md index 42f326c..b01f17e 100644 --- a/examples/nginx-local/README.md +++ b/examples/nginx-local/README.md @@ -1,34 +1,34 @@ # Example: nginx + htpasswd -This Example shows how to use ska with nginx and ldap using docker. +This Example shows how to use sca with nginx and ldap using docker. ## Prepare setup 1. Start system using `docker-compose up -d` 1. Visit http://localhost -1. Login using one of the following credentials (Only keys-sync account exists at first): +1. Login using one of the following credentials (Only cert-sync account exists at first): |Username|Password|Type| |---|---|---| -|keys-sync|password|admin| +|cert-sync|password|admin| |rainbow|password|admin| |proceme|password|user| If something goes wrong, check the log using: ``` -docker logs -f nginx-local_ska_1 +docker logs -f nginx-local_sca_1 ``` -## Using ska +## Using sca -_The `keys-sync` user should only be used for the first setup. Afterwards its best to create a dedicated account per user._ +_The `cert-sync` user should only be used for the first setup. Afterwards its best to create a dedicated account per user._ -1. Login using the admin account `keys-sync`. +1. Login using the admin account `cert-sync`. 1. Create user `rainbow` as admin and user `proceme` as user at http://localhost/users#add 1. Add the server `test.example.com` at http://localhost/servers#add -1. Ska should be able to connet to the system and update its authorized_keys file. You can verify this by checking whether there is an `Synced successfully` next to the server. +1. Sca should be able to connet to the system and update its authorized_keys file. You can verify this by checking whether there is an `Synced successfully` next to the server. ## Add/Change passwords for users -1. Either install `htpasswd` on your system or connect to the nginx container using `docker exec -it nginx-local_ska_1 /bin/ash` and install it there with `apk add apache2-utils` +1. Either install `htpasswd` on your system or connect to the nginx container using `docker exec -it nginx-local_sca_1 /bin/ash` and install it there with `apk add apache2-utils` 1. Run `htpasswd` on the htpasswd file. Inside the container it is `htpasswd /allowed_users ` diff --git a/examples/nginx-local/docker-compose.yml b/examples/nginx-local/docker-compose.yml index 9778ca2..aa9ef30 100644 --- a/examples/nginx-local/docker-compose.yml +++ b/examples/nginx-local/docker-compose.yml @@ -2,14 +2,14 @@ version: '2.2' services: test: image: alpine:3.8 - command: /bin/ash -c "(id keys-sync || adduser -h /var/local/keys-sync -S -D -s /bin/sh keys-sync) && chmod 711 /var/local/keys-sync && cp /key /var/local/keys-sync/keys-sync && chown keys-sync:nogroup /var/local/keys-sync/keys-sync && chmod 644 /var/local/keys-sync/keys-sync && apk add openssh && ssh-keygen -A && sed -i -e '/#StrictModes/ s/.*/StrictModes no/' /etc/ssh/sshd_config && sed -i -e '/AuthorizedKeysFile/ s/.*/AuthorizedKeysFile \/var\/local\/keys-sync\/%u/' /etc/ssh/sshd_config && passwd keys-sync -d test && passwd root -d test && /usr/sbin/sshd -D" + command: /bin/ash -c "(id cert-sync || adduser -h /var/local/cert-sync -S -D -s /bin/sh cert-sync) && chmod 711 /var/local/cert-sync && cp /key /var/local/cert-sync/cert-sync && chown cert-sync:nogroup /var/local/cert-sync/cert-sync && chmod 644 /var/local/cert-sync/cert-sync && apk add openssh && ssh-keygen -A && sed -i -e '/#StrictModes/ s/.*/StrictModes no/' /etc/ssh/sshd_config && sed -i -e '/AuthorizedKeysFile/ s/.*/AuthorizedKeysFile \/var\/local\/cert-sync\/%u/' /etc/ssh/sshd_config && passwd cert-sync -d test && passwd root -d test && /usr/sbin/sshd -D" restart: always expose: - "22" depends_on: - - ska-php + - sca-php volumes: - - ../shared/config-local/keys-sync.pub:/key:ro + - ../shared/config-local/cert-sync.pub:/key:ro networks: net: aliases: @@ -19,49 +19,49 @@ services: image: mwader/postfix-relay restart: always environment: - - POSTFIX_myhostname=ska.example.de + - POSTFIX_myhostname=sca.example.de - POSTFIX_mynetworks=0.0.0.0/0 expose: - "25" networks: - net - ska-db: + sca-db: image: mariadb restart: always environment: - MYSQL_ROOT_PASSWORD=root-password - - MYSQL_DATABASE=ska-db - - MYSQL_USER=ska-user + - MYSQL_DATABASE=sca-db + - MYSQL_USER=sca-user - MYSQL_PASSWORD=password volumes: - ./db:/var/lib/mysql:rw networks: - net - ska-php: + sca-php: build: context: ../../docker restart: always depends_on: - - ska-db + - sca-db - mail volumes: - - ../shared/config-local/:/ska/config/:rw + - ../shared/config-local/:/sca/config/:rw - ../shared/ssmtp.conf:/etc/ssmtp/ssmtp.conf:ro - ./public_html:/public_html:rw networks: - net - ska: + sca: image: nginx:alpine restart: always ports: - "80:80" depends_on: - - ska-php + - sca-php volumes: - - ./public_html:/ska/public_html:ro + - ./public_html:/sca/public_html:ro - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro - ./htpasswd.conf:/allowed_users:rw networks: diff --git a/examples/nginx-local/htpasswd.conf b/examples/nginx-local/htpasswd.conf index 1e9bc10..c8e4d97 100644 --- a/examples/nginx-local/htpasswd.conf +++ b/examples/nginx-local/htpasswd.conf @@ -1,4 +1,4 @@ -keys-sync:$apr1$PqjgspFz$DBUOsj/1yVzIf3v9Kv7wJ0 +cert-sync:$apr1$PqjgspFz$DBUOsj/1yVzIf3v9Kv7wJ0 rainbow:$apr1$uBkkgFwm$zrLgjA/5R8V1P0F/XHKUV0 proceme:$apr1$SWQKwjuM$D0FWOsBAD4lTdcYrtbZ0j0 speecif:$apr1$MCOzQE15$K4uameMTLTf3fJDSj/XfE. diff --git a/examples/nginx-local/nginx.conf b/examples/nginx-local/nginx.conf index b6f4af1..7de4b7a 100644 --- a/examples/nginx-local/nginx.conf +++ b/examples/nginx-local/nginx.conf @@ -1,12 +1,12 @@ server { listen 80; - server_name ska.example.com; + server_name sca.example.com; - root /ska/public_html; + root /sca/public_html; index init.php; - auth_basic "SSH Key Authority"; + auth_basic "SSL Cert Authority"; auth_basic_user_file /allowed_users; location / { @@ -20,7 +20,7 @@ server { location /init.php { # Mitigate https://httpoxy.org/ vulnerabilities fastcgi_param HTTP_PROXY ""; - fastcgi_pass ska-php:9000; + fastcgi_pass sca-php:9000; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; diff --git a/examples/shared/config-ldap/keys-sync b/examples/shared/config-ldap/cert-sync similarity index 100% rename from examples/shared/config-ldap/keys-sync rename to examples/shared/config-ldap/cert-sync diff --git a/examples/shared/config-ldap/keys-sync.pub b/examples/shared/config-ldap/cert-sync.pub similarity index 95% rename from examples/shared/config-ldap/keys-sync.pub rename to examples/shared/config-ldap/cert-sync.pub index dce6e2a..1a104d5 100644 --- a/examples/shared/config-ldap/keys-sync.pub +++ b/examples/shared/config-ldap/cert-sync.pub @@ -1 +1 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpq2xgbC14Lhf5oiw1OGW5dmWH1Jt1BMHdnLwShFyApIRB2/Cyfnp7dIsJO0tUZgIPuXltO+O4kgbRUYoYbxliIRIdBEGqbq3TNYQkyI4LSZS3fFbhglxUWGyTnhaFUxZbsiuPH7CT1Vtfg56WVTQVlKMCz6FJ6ucA6dWhI1edJoLmGir3xBZ2VTAj6k6Qp/IgjhhhxJo8HI0D93HgfLhxWohpUi8N/hKJgeepsIJltJlytANb+kuerqzh7cu9g1oHYFYuVtp3xi4lEFFKn0Le2FXUSZggNB2WTqyDR+QUZiL6NYVYpq0DS7squ3kOHcF542AWp6B+xvcTU/z6BSnp root@ska.example.com +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpq2xgbC14Lhf5oiw1OGW5dmWH1Jt1BMHdnLwShFyApIRB2/Cyfnp7dIsJO0tUZgIPuXltO+O4kgbRUYoYbxliIRIdBEGqbq3TNYQkyI4LSZS3fFbhglxUWGyTnhaFUxZbsiuPH7CT1Vtfg56WVTQVlKMCz6FJ6ucA6dWhI1edJoLmGir3xBZ2VTAj6k6Qp/IgjhhhxJo8HI0D93HgfLhxWohpUi8N/hKJgeepsIJltJlytANb+kuerqzh7cu9g1oHYFYuVtp3xi4lEFFKn0Le2FXUSZggNB2WTqyDR+QUZiL6NYVYpq0DS7squ3kOHcF542AWp6B+xvcTU/z6BSnp root@sca.example.com diff --git a/examples/shared/config-ldap/config.ini b/examples/shared/config-ldap/config.ini index 8a2a04d..8984935 100644 --- a/examples/shared/config-ldap/config.ini +++ b/examples/shared/config-ldap/config.ini @@ -1,7 +1,7 @@ -; SSH Key Authority config file +; SCA Key Authority config file [web] enabled = 1 -baseurl = https://ska.example.com +baseurl = https://sca.example.com logo = /logo-header-itmettke.png ; footer may contain HTML. Literal & " < and > should be escaped as & ; " < $gt; @@ -14,23 +14,17 @@ footer = 'Developed by Mar ; used on e.g. alpine timeout_util = BusyBox -key_expiration_enabled = 0 -key_expiration_days = 180 - -minimum_rsa_key_size = 4096 -minimum_ecdsa_key_size = 384 - [security] -; It is important that SKA is able to verify that it has connected to the +; It is important that SCA is able to verify that it has connected to the ; server that it expected to connect to (otherwise it could be tricked into ; syncing the wrong keys to a server). The simplest way to accomplish this is ; through SSH host key verification. Setting either of the 2 options below to ; '0' can weaken the protection that SSH host key verification provides. -; Determine who can reset a server's SSH host key in SKA: +; Determine who can reset a server's SSH host key in SCA: ; 0: Allow server admins to reset the SSH host key for servers that they ; administer -; 1: Full SKA admin access is required to reset a server's host key +; 1: Full SCA admin access is required to reset a server's host key host_key_reset_restriction = 1 ; Determine what happens if multiple servers have the same SSH host key: @@ -47,46 +41,36 @@ host_key_collision_protection = 1 ; Determine how hostname verification is performed: ; 0: Do not perform hostname verification ; 1: Compare with the result of `hostname -f` -; 2: Compare with /var/local/keys-sync/.hostnames, fall back to `hostname -f` +; 2: Compare with /var/local/cert-sync/.hostnames, fall back to `hostname -f` ; if the file does not exist -; 3: Compare with /var/local/keys-sync/.hostnames, abort sync if the file +; 3: Compare with /var/local/cert-sync/.hostnames, abort sync if the file ; does not exist ; The last option provides the most solid verification, as a server will only ; be synced to if it has been explicitly allowed on the server itself. hostname_verification = 0 -[defaults] -; This setting will cause new servers to always have a managed account called -; "root" and for that account to be automatically added into the -; "root-accounts" group: -; -; account_groups[root] = "root-accounts" -; -; Any number of these can be specified -account_groups[root] = "accounts-root" - [email] enabled = 1 ; The mail address that outgoing mails will be sent from -from_address = ska@example.com -from_name = "SSH Key Authority system" +from_address = sca@example.com +from_name = "SCA Key Authority system" ; Where to mail security notifications to report_address = reports@example.com -report_name = "SSH Key Authority reports" +report_name = "SCA Key Authority reports" ; Where users should contact for help admin_address = admin@example.com -admin_name = "SSH Key Authority administrators" +admin_name = "SCA Key Authority administrators" ; You can use the reroute directive to redirect all outgoing mail to a single ; mail address - typically for temporary testing purposes ;reroute = test@example.com [database] ; Connection details to the MySQL database -hostname = ska-db +hostname = sca-db port = 3306 -username = ska-user +username = sca-user password = password -database = ska-db +database = sca-db [ldap] enabled = 1 @@ -114,48 +98,16 @@ user_name = cn user_email = mail ;user_superior = superioremployee -; If inactive users exist in your LDAP directory, filter with the following -; settings: -; Field to filter on: -;user_active = organizationalstatus -; Use *one* of user_active_true or user_active_false -; user_active_true means user is active if the user_active field equals its -; value -;user_active_true = 'current' -; user_active_false means user is active if the user_active field does not -; equal its value -;user_active_false = 'former' - -; Group membership attributes. Examples below are for typical setups: -; -; POSIX groups -; group_member = memberUid -; group_member_value = uid -; -; Group-of-names groups -; group_member = member -; group_member_value = dn -; -; Attribute of group where members are stored -group_member = member -; User attribute to compare with -group_member_value = dn - -; Members of admin_group are given full admin access to SSH Key Authority web -; interface -admin_group_cn = admin -; By default only the admin_group_cn will be synced. This option enabled -; synchronisation of every group a user is in -full_group_sync = 1 +filter = admin [inventory] -; SSH Key Authority will read the contents of the file /etc/uuid (if it +; SCA Key Authority will read the contents of the file /etc/uuid (if it ; exists) when syncing with a server. If a value is found, it can be used as a ; link to an inventory system. ; %s in the url directive will be replaced with the value found in /etc/uuid ;url = "https://inventory.example.com/device/%s" [gpg] -; SSH Key Authority can GPG sign outgoing emails sent from the +; SCA Key Authority can GPG sign outgoing emails sent from the ; email.from_address. To do this it needs to know an appropriate key ID to use ;key_id = 0123456789ABCDEF0123456789ABCDEF01234567 diff --git a/examples/shared/config-local/keys-sync b/examples/shared/config-local/cert-sync similarity index 100% rename from examples/shared/config-local/keys-sync rename to examples/shared/config-local/cert-sync diff --git a/examples/shared/config-local/keys-sync.pub b/examples/shared/config-local/cert-sync.pub similarity index 95% rename from examples/shared/config-local/keys-sync.pub rename to examples/shared/config-local/cert-sync.pub index dce6e2a..1a104d5 100644 --- a/examples/shared/config-local/keys-sync.pub +++ b/examples/shared/config-local/cert-sync.pub @@ -1 +1 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpq2xgbC14Lhf5oiw1OGW5dmWH1Jt1BMHdnLwShFyApIRB2/Cyfnp7dIsJO0tUZgIPuXltO+O4kgbRUYoYbxliIRIdBEGqbq3TNYQkyI4LSZS3fFbhglxUWGyTnhaFUxZbsiuPH7CT1Vtfg56WVTQVlKMCz6FJ6ucA6dWhI1edJoLmGir3xBZ2VTAj6k6Qp/IgjhhhxJo8HI0D93HgfLhxWohpUi8N/hKJgeepsIJltJlytANb+kuerqzh7cu9g1oHYFYuVtp3xi4lEFFKn0Le2FXUSZggNB2WTqyDR+QUZiL6NYVYpq0DS7squ3kOHcF542AWp6B+xvcTU/z6BSnp root@ska.example.com +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpq2xgbC14Lhf5oiw1OGW5dmWH1Jt1BMHdnLwShFyApIRB2/Cyfnp7dIsJO0tUZgIPuXltO+O4kgbRUYoYbxliIRIdBEGqbq3TNYQkyI4LSZS3fFbhglxUWGyTnhaFUxZbsiuPH7CT1Vtfg56WVTQVlKMCz6FJ6ucA6dWhI1edJoLmGir3xBZ2VTAj6k6Qp/IgjhhhxJo8HI0D93HgfLhxWohpUi8N/hKJgeepsIJltJlytANb+kuerqzh7cu9g1oHYFYuVtp3xi4lEFFKn0Le2FXUSZggNB2WTqyDR+QUZiL6NYVYpq0DS7squ3kOHcF542AWp6B+xvcTU/z6BSnp root@sca.example.com diff --git a/examples/shared/config-local/config.ini b/examples/shared/config-local/config.ini index 78adde6..fc47289 100644 --- a/examples/shared/config-local/config.ini +++ b/examples/shared/config-local/config.ini @@ -1,7 +1,7 @@ -; SSH Key Authority config file +; SCA Key Authority config file [web] enabled = 1 -baseurl = https://ska.example.com +baseurl = https://sca.example.com logo = /logo-header-itmettke.png ; footer may contain HTML. Literal & " < and > should be escaped as & ; " < $gt; @@ -14,23 +14,17 @@ footer = 'Developed by Mar ; used on e.g. alpine timeout_util = BusyBox -key_expiration_enabled = 0 -key_expiration_days = 180 - -minimum_rsa_key_size = 4096 -minimum_ecdsa_key_size = 384 - [security] -; It is important that SKA is able to verify that it has connected to the +; It is important that SCA is able to verify that it has connected to the ; server that it expected to connect to (otherwise it could be tricked into ; syncing the wrong keys to a server). The simplest way to accomplish this is ; through SSH host key verification. Setting either of the 2 options below to ; '0' can weaken the protection that SSH host key verification provides. -; Determine who can reset a server's SSH host key in SKA: +; Determine who can reset a server's SSH host key in SCA: ; 0: Allow server admins to reset the SSH host key for servers that they ; administer -; 1: Full SKA admin access is required to reset a server's host key +; 1: Full SCA admin access is required to reset a server's host key host_key_reset_restriction = 1 ; Determine what happens if multiple servers have the same SSH host key: @@ -47,46 +41,36 @@ host_key_collision_protection = 1 ; Determine how hostname verification is performed: ; 0: Do not perform hostname verification ; 1: Compare with the result of `hostname -f` -; 2: Compare with /var/local/keys-sync/.hostnames, fall back to `hostname -f` +; 2: Compare with /var/local/cert-sync/.hostnames, fall back to `hostname -f` ; if the file does not exist -; 3: Compare with /var/local/keys-sync/.hostnames, abort sync if the file +; 3: Compare with /var/local/cert-sync/.hostnames, abort sync if the file ; does not exist ; The last option provides the most solid verification, as a server will only ; be synced to if it has been explicitly allowed on the server itself. hostname_verification = 0 -[defaults] -; This setting will cause new servers to always have a managed account called -; "root" and for that account to be automatically added into the -; "root-accounts" group: -; -; account_groups[root] = "root-accounts" -; -; Any number of these can be specified -account_groups[root] = "accounts-root" - [email] enabled = 1 ; The mail address that outgoing mails will be sent from -from_address = ska@example.com -from_name = "SSH Key Authority system" +from_address = sca@example.com +from_name = "SCA Key Authority system" ; Where to mail security notifications to report_address = reports@example.com -report_name = "SSH Key Authority reports" +report_name = "SCA Key Authority reports" ; Where users should contact for help admin_address = admin@example.com -admin_name = "SSH Key Authority administrators" +admin_name = "SCA Key Authority administrators" ; You can use the reroute directive to redirect all outgoing mail to a single ; mail address - typically for temporary testing purposes ;reroute = test@example.com [database] ; Connection details to the MySQL database -hostname = ska-db +hostname = sca-db port = 3306 -username = ska-user +username = sca-user password = password -database = ska-db +database = sca-db [ldap] enabled = 0 @@ -114,48 +98,16 @@ user_name = cn user_email = mail ;user_superior = superioremployee -; If inactive users exist in your LDAP directory, filter with the following -; settings: -; Field to filter on: -;user_active = organizationalstatus -; Use *one* of user_active_true or user_active_false -; user_active_true means user is active if the user_active field equals its -; value -;user_active_true = 'current' -; user_active_false means user is active if the user_active field does not -; equal its value -;user_active_false = 'former' - -; Group membership attributes. Examples below are for typical setups: -; -; POSIX groups -; group_member = memberUid -; group_member_value = uid -; -; Group-of-names groups -; group_member = member -; group_member_value = dn -; -; Attribute of group where members are stored -group_member = memberUid -; User attribute to compare with -group_member_value = uid - -; Members of admin_group are given full admin access to SSH Key Authority web -; interface -admin_group_cn = ska-administrators -; By default only the admin_group_cn will be synced. This option enabled -; synchronisation of every group a user is in -full_group_sync = 0 +filter = sca-administrators [inventory] -; SSH Key Authority will read the contents of the file /etc/uuid (if it +; SCA Key Authority will read the contents of the file /etc/uuid (if it ; exists) when syncing with a server. If a value is found, it can be used as a ; link to an inventory system. ; %s in the url directive will be replaced with the value found in /etc/uuid ;url = "https://inventory.example.com/device/%s" [gpg] -; SSH Key Authority can GPG sign outgoing emails sent from the +; SCA Key Authority can GPG sign outgoing emails sent from the ; email.from_address. To do this it needs to know an appropriate key ID to use ;key_id = 0123456789ABCDEF0123456789ABCDEF01234567 diff --git a/examples/shared/ssmtp.conf b/examples/shared/ssmtp.conf index 7e1ec6b..8129b1c 100644 --- a/examples/shared/ssmtp.conf +++ b/examples/shared/ssmtp.conf @@ -1,4 +1,4 @@ mailhub=mail rewriteDomain=example.com -hostname="ska" +hostname="sca" FromLineOverride=YES diff --git a/migrations/002.php b/migrations/002.php index 025e5d3..e889788 100644 --- a/migrations/002.php +++ b/migrations/002.php @@ -1,382 +1,238 @@ database->query('SELECT * FROM entity'); -} catch(mysqli_sql_exception $e) { - $this->database->query(" - CREATE TABLE `access` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `source_entity_id` int(10) unsigned NOT NULL, - `dest_entity_id` int(10) unsigned NOT NULL, - `grant_date` datetime NOT NULL, - `granted_by` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `source_entity_id_dest_entity_id` (`source_entity_id`, `dest_entity_id`), - KEY `FK_access_entity_2` (`dest_entity_id`), - KEY `FK_access_entity_3` (`granted_by`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT - "); - - $this->database->query(" - CREATE TABLE `access_option` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `access_id` int(10) unsigned NOT NULL, - `option` enum('command', 'from', 'no-agent-forwarding', 'no-port-forwarding', 'no-pty', 'no-X11-forwarding') NOT NULL, - `value` text, - PRIMARY KEY (`id`), - UNIQUE KEY `access_id_option` (`access_id`, `option`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `access_request` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `source_entity_id` int(10) unsigned NOT NULL, - `dest_entity_id` int(10) unsigned NOT NULL, - `request_date` datetime NOT NULL, - `requested_by` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `source_entity_id_dest_entity_id` (`source_entity_id`, `dest_entity_id`), - KEY `FK_access_request_entity_2` (`dest_entity_id`), - KEY `FK_access_request_entity_3` (`requested_by`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT; - "); - - $this->database->query(" - CREATE TABLE `entity` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `type` enum('user','server account', 'group') NOT NULL, - PRIMARY KEY (`id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `entity_admin` ( - `entity_id` int(10) unsigned NOT NULL, - `admin` int(10) unsigned NOT NULL, - PRIMARY KEY (`entity_id`, `admin`), - KEY `FK_entity_admin_entity_2` (`admin`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT; - "); - - $this->database->query(" - CREATE TABLE `entity_event` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `entity_id` int(10) unsigned NOT NULL, - `actor_id` int(10) unsigned NOT NULL, - `date` datetime NOT NULL, - `details` mediumtext NOT NULL, - PRIMARY KEY (`id`), - KEY `FK_entity_event_entity_id` (`entity_id`), - KEY `FK_entity_event_actor_id` (`actor_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `group` ( - `entity_id` int(10) unsigned NOT NULL, - `name` varchar(100) NOT NULL, - `active` tinyint(1) unsigned NOT NULL DEFAULT '1', - `system` tinyint(1) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`entity_id`), - UNIQUE KEY `name` (`name`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `group_event` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `group` int(10) unsigned NOT NULL, - `entity_id` int(10) unsigned NOT NULL, - `date` datetime NOT NULL, - `details` mediumtext NOT NULL, - PRIMARY KEY (`id`), - KEY `FK_group_event_group` (`group`), - KEY `FK_group_event_entity` (`entity_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT; - "); - - $this->database->query(" - CREATE TABLE `group_member` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `group` int(10) unsigned NOT NULL, - `entity_id` int(10) unsigned NOT NULL, - `add_date` datetime NOT NULL, - `added_by` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `group_entity_id` (`group`, `entity_id`), - KEY `FK_group_member_entity` (`entity_id`), - KEY `FK_group_member_entity_2` (`added_by`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT; - "); - - $this->database->query(" - CREATE TABLE `public_key` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `entity_id` int(10) unsigned NOT NULL, - `type` varchar(30) NOT NULL, - `keydata` mediumtext NOT NULL, - `comment` mediumtext NOT NULL, - `keysize` int(11) DEFAULT NULL, - `fingerprint_md5` char(47) DEFAULT NULL, - `fingerprint_sha256` varchar(50) DEFAULT NULL, - `randomart_md5` text, - `randomart_sha256` text, - PRIMARY KEY (`id`), - KEY `FK_public_key_entity` (`entity_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `public_key_dest_rule` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `public_key_id` int(10) unsigned NOT NULL, - `account_name_filter` varchar(50) NOT NULL, - `hostname_filter` varchar(255) NOT NULL, - PRIMARY KEY (`id`), - KEY `FK_public_key_dest_rule_public_key` (`public_key_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `public_key_signature` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `public_key_id` int(10) unsigned NOT NULL, - `signature` blob NOT NULL, - `upload_date` datetime NOT NULL, - `fingerprint` varchar(50) NOT NULL, - `sign_date` datetime NOT NULL, - PRIMARY KEY (`id`), - KEY `FK_public_key_signature_public_key` (`public_key_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `server` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `uuid` varchar(36) DEFAULT NULL, - `hostname` varchar(150) NOT NULL, - `ip_address` varchar(64) DEFAULT NULL, - `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', - `key_management` enum('none', 'keys', 'other', 'decommissioned') NOT NULL DEFAULT 'keys', - `authorization` enum('manual', 'automatic LDAP', 'manual LDAP') NOT NULL DEFAULT 'manual', - `use_sync_client` enum('no', 'yes') NOT NULL DEFAULT 'no', - `sync_status` enum('not synced yet', 'sync success', 'sync failure', 'sync warning') NOT NULL DEFAULT 'not synced yet', - `configuration_system` enum('unknown', 'cf-sysadmin', 'puppet-devops', 'puppet-miniops', 'puppet-tvstore', 'none') NOT NULL DEFAULT 'unknown', - `custom_keys` enum('not allowed', 'allowed') NOT NULL DEFAULT 'not allowed', - `rsa_key_fingerprint` char(32) DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `hostname` (`hostname`), - KEY `ip_address` (`ip_address`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `server_account` ( - `entity_id` int(10) unsigned NOT NULL, - `server_id` int(10) unsigned NOT NULL, - `name` varchar(50) DEFAULT NULL, - `sync_status` enum('not synced yet', 'sync success', 'sync failure', 'sync warning', 'proposed') NOT NULL DEFAULT 'not synced yet', - `active` tinyint(1) unsigned NOT NULL DEFAULT '1', - PRIMARY KEY (`entity_id`), - UNIQUE KEY `server_id_name` (`server_id`, `name`), - KEY `FK_server_account_server` (`server_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `server_admin` ( - `server_id` int(10) unsigned NOT NULL, - `entity_id` int(10) unsigned NOT NULL, - PRIMARY KEY (`server_id`,`entity_id`), - KEY `FK_server_admin_entity` (`entity_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `server_event` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `server_id` int(10) unsigned NOT NULL, - `actor_id` int(10) unsigned NOT NULL, - `date` datetime NOT NULL, - `details` mediumtext NOT NULL, - PRIMARY KEY (`id`), - KEY `FK_server_log_server` (`server_id`), - KEY `FK_server_event_actor_id` (`actor_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `server_ldap_access_option` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `server_id` int(10) unsigned NOT NULL, - `option` enum('command', 'from', 'no-agent-forwarding', 'no-port-forwarding', 'no-pty', 'no-X11-forwarding') NOT NULL, - `value` text, - PRIMARY KEY (`id`), - UNIQUE KEY `server_id_option` (`server_id`, `option`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `server_note` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `server_id` int(10) unsigned NOT NULL, - `entity_id` int(10) unsigned NOT NULL, - `date` datetime NOT NULL, - `note` mediumtext NOT NULL, - PRIMARY KEY (`id`), - KEY `FK_server_note_server` (`server_id`), - KEY `FK_server_note_user` (`entity_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `sync_request` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `server_id` int(10) unsigned NOT NULL, - `account_name` varchar(50) DEFAULT NULL, - `processing` tinyint(1) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `server_id_account_name` (`server_id`,`account_name`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `user` ( - `entity_id` int(10) unsigned NOT NULL, - `uid` varchar(50) NOT NULL, - `name` varchar(100) NOT NULL, - `email` varchar(100) NOT NULL, - `superior_entity_id` int(10) unsigned DEFAULT NULL, - `auth_realm` enum('LDAP','local','external') NOT NULL DEFAULT 'LDAP', - `active` tinyint(1) unsigned NOT NULL DEFAULT '1', - `admin` tinyint(1) unsigned NOT NULL DEFAULT '0', - `developer` tinyint(1) unsigned NOT NULL DEFAULT '0', - `force_disable` tinyint(1) unsigned NOT NULL DEFAULT '0', - `csrf_token` binary(128) DEFAULT NULL, - PRIMARY KEY (`entity_id`), - UNIQUE KEY `uid` (`uid`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - CREATE TABLE `user_alert` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `entity_id` int(10) unsigned NOT NULL, - `class` varchar(15) NOT NULL, - `content` mediumtext NOT NULL, - `escaping` int(10) unsigned NOT NULL DEFAULT '1', - PRIMARY KEY (`id`), - KEY `FK_user_alert_entity` (`entity_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - "); - - $this->database->query(" - ALTER TABLE `access` - ADD CONSTRAINT `FK_access_entity` FOREIGN KEY (`source_entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE, - ADD CONSTRAINT `FK_access_entity_2` FOREIGN KEY (`dest_entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE, - ADD CONSTRAINT `FK_access_entity_3` FOREIGN KEY (`granted_by`) REFERENCES `entity` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `access_option` - ADD CONSTRAINT `FK_access_option_access` FOREIGN KEY (`access_id`) REFERENCES `access` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `access_request` - ADD CONSTRAINT `FK_access_request_entity` FOREIGN KEY (`source_entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE, - ADD CONSTRAINT `FK_access_request_entity_2` FOREIGN KEY (`dest_entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE, - ADD CONSTRAINT `FK_access_request_entity_3` FOREIGN KEY (`requested_by`) REFERENCES `entity` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `entity_admin` - ADD CONSTRAINT `FK_entity_admin_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE, - ADD CONSTRAINT `FK_entity_admin_entity_2` FOREIGN KEY (`admin`) REFERENCES `entity` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `entity_event` - ADD CONSTRAINT `FK_entity_event_actor_id` FOREIGN KEY (`actor_id`) REFERENCES `entity` (`id`), - ADD CONSTRAINT `FK_entity_event_entity_id` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) - "); - - $this->database->query(" - ALTER TABLE `group` - ADD CONSTRAINT `FK_group_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `group_event` - ADD CONSTRAINT `FK_group_event_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`), - ADD CONSTRAINT `FK_group_event_group` FOREIGN KEY (`group`) REFERENCES `group` (`entity_id`) - "); - - $this->database->query(" - ALTER TABLE `group_member` - ADD CONSTRAINT `FK_group_member_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE, - ADD CONSTRAINT `FK_group_member_entity_2` FOREIGN KEY (`added_by`) REFERENCES `entity` (`id`), - ADD CONSTRAINT `FK_group_member_group` FOREIGN KEY (`group`) REFERENCES `group` (`entity_id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `public_key` - ADD CONSTRAINT `FK_public_key_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `public_key_dest_rule` - ADD CONSTRAINT `FK_public_key_dest_rule_public_key` FOREIGN KEY (`public_key_id`) REFERENCES `public_key` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `public_key_signature` - ADD CONSTRAINT `FK_public_key_signature_public_key` FOREIGN KEY (`public_key_id`) REFERENCES `public_key` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `server_account` - ADD CONSTRAINT `FK_server_account_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE, - ADD CONSTRAINT `FK_server_account_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `server_admin` - ADD CONSTRAINT `FK_server_admin_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE, - ADD CONSTRAINT `FK_server_admin_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `server_event` - ADD CONSTRAINT `FK_server_event_actor_id` FOREIGN KEY (`actor_id`) REFERENCES `entity` (`id`), - ADD CONSTRAINT `FK_server_log_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) - "); - - $this->database->query(" - ALTER TABLE `server_ldap_access_option` - ADD CONSTRAINT `FK_server_ldap_access_option_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `server_note` - ADD CONSTRAINT `FK_server_note_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`), - ADD CONSTRAINT `FK_server_note_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `sync_request` - ADD CONSTRAINT `FK_sync_request_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `user` - ADD CONSTRAINT `FK_user_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE - "); - - $this->database->query(" - ALTER TABLE `user_alert` - ADD CONSTRAINT `FK_user_alert_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE - "); -} \ No newline at end of file +$migration_name = 'Initial setup'; + +$this->database->query(" +CREATE TABLE `server` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(36) DEFAULT NULL, + `hostname` varchar(150) NOT NULL, + `ip_address` varchar(64) DEFAULT NULL, + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', + `rsa_key_fingerprint` char(32) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `hostname` (`hostname`), + KEY `server_uuid` (`uuid`), + KEY `server_ip_address` (`ip_address`), + KEY `server_rsa_key_fingerprint` (`rsa_key_fingerprint`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `server_event` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `server_id` int(10) unsigned NOT NULL, + `actor_id` int(10) unsigned, + `date` datetime NOT NULL, + `details` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_server_log_server` (`server_id`), + KEY `FK_server_event_actor_id` (`actor_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `server_note` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `server_id` int(10) unsigned NOT NULL, + `user_id` int(10) unsigned, + `date` datetime NOT NULL, + `note` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_server_note_server` (`server_id`), + KEY `FK_server_note_user` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `server_profile` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `server_id` int(10) unsigned NOT NULL, + `profile_id` int(10) unsigned NOT NULL, + `sync_status` enum('not synced yet', 'sync success', 'sync failure', 'sync warning') NOT NULL DEFAULT 'not synced yet', + PRIMARY KEY (`id`), + UNIQUE KEY `server_profile_id` (`server_id`, `profile_id`), + KEY `FK_server_profile_server` (`server_id`), + KEY `FK_server_profile_profile` (`profile_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `profile` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(150) NOT NULL, + `certificate_id` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`), + KEY `profile_certificate_id` (`certificate_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `service_profile` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `service_id` int(10) unsigned NOT NULL, + `profile_id` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `service_profile_id` (`service_id`, `profile_id`), + KEY `FK_service_profile_service` (`service_id`), + KEY `FK_service_profile_profile` (`profile_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `service` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(150) NOT NULL, + `restart_script_id` int(10) unsigned DEFAULT NULL, + `status_script_id` int(10) unsigned DEFAULT NULL, + `check_script_id` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `service_restart_script_id` (`restart_script_id`), + KEY `service_status_script_id` (`status_script_id`), + KEY `service_check_script_id` (`check_script_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `certificate` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `private` text NOT NULL, + `chain` text NOT NULL, + `fullchain` text NOT NULL, + `fingerprint` char(32) NOT NULL, + `expiration` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `environment_variables` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(150) NOT NULL, + `service_id` int(10) unsigned NOT NULL, + `value` varchar(150) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `service_profile_id` (`name`, `service_id`), + KEY `FK_env_service_id` (`service_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `script` ( + `id` int(10) unsigned NOT NULL, + `name` varchar(150) NOT NULL, + `content` text NOT NULL, + `type` enum('restart', 'status', 'check') NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `sync_request` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `server_id` int(10) unsigned NOT NULL, + `profile_id` int(10) unsigned NOT NULL, + `processing` tinyint(1) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `server_id_account_name` (`server_id`,`profile_id`), + KEY `FK_sync_request_server` (`server_id`), + KEY `FK_sync_request_profile` (`profile_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `user` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `uid` varchar(50) NOT NULL, + `name` varchar(100) NOT NULL, + `email` varchar(100) NOT NULL, + `auth_realm` enum('LDAP','local','external') NOT NULL DEFAULT 'local', + `active` tinyint(1) unsigned NOT NULL DEFAULT '1', + `developer` tinyint(1) unsigned NOT NULL DEFAULT '0', + `force_disable` tinyint(1) unsigned NOT NULL DEFAULT '0', + `csrf_token` binary(128) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uid` (`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `user_event` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL, + `actor_id` int(10) unsigned, + `date` datetime NOT NULL, + `details` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_user_event_user_id` (`user_id`), + KEY `FK_user_event_actor_id` (`actor_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +CREATE TABLE `user_alert` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL, + `class` varchar(15) NOT NULL, + `content` mediumtext NOT NULL, + `escaping` int(10) unsigned NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + KEY `FK_user_alert_user` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +$this->database->query(" +ALTER TABLE `server_event` + ADD CONSTRAINT `FK_server_event_actor_id` FOREIGN KEY (`actor_id`) REFERENCES `user` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_server_log_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE +"); + +$this->database->query(" +ALTER TABLE `server_note` + ADD CONSTRAINT `FK_server_note_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_server_note_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE +"); + +$this->database->query(" +ALTER TABLE `server_profile` + ADD CONSTRAINT `FK_server_profile_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE, + ADD CONSTRAINT `FK_server_profile_profile` FOREIGN KEY (`profile_id`) REFERENCES `profile` (`id`) ON DELETE CASCADE +"); + +$this->database->query(" +ALTER TABLE `profile` + ADD CONSTRAINT `FK_profile_certificate` FOREIGN KEY (`certificate_id`) REFERENCES `certificate` (`id`) +"); + +$this->database->query(" +ALTER TABLE `service` + ADD CONSTRAINT `FK_service_restart_script` FOREIGN KEY (`restart_script_id`) REFERENCES `script` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_service_status_script` FOREIGN KEY (`status_script_id`) REFERENCES `script` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_service_check_script` FOREIGN KEY (`check_script_id`) REFERENCES `script` (`id`) ON DELETE SET NULL +"); + +$this->database->query(" +ALTER TABLE `environment_variables` + ADD CONSTRAINT `FK_env_service` FOREIGN KEY (`service_id`) REFERENCES `service` (`id`) ON DELETE CASCADE +"); + +$this->database->query(" +ALTER TABLE `sync_request` + ADD CONSTRAINT `FK_sync_request_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE, + ADD CONSTRAINT `FK_sync_request_profile` FOREIGN KEY (`profile_id`) REFERENCES `profile` (`id`) ON DELETE CASCADE +"); + +$this->database->query(" +ALTER TABLE `user_event` + ADD CONSTRAINT `FK_user_event_actor_id` FOREIGN KEY (`actor_id`) REFERENCES `user` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_user_event_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE +"); + +$this->database->query(" +ALTER TABLE `user_alert` + ADD CONSTRAINT `FK_user_alert_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE +"); + +$this->database->query(" +INSERT INTO user SET uid = 'cert-sync', name = 'Synchronization script', email = '', auth_realm = 'local' +"); diff --git a/migrations/003.php b/migrations/003.php deleted file mode 100644 index e930dfd..0000000 --- a/migrations/003.php +++ /dev/null @@ -1,6 +0,0 @@ -database->query(" -ALTER TABLE `server` ADD COLUMN `port` int(10) unsigned NOT NULL DEFAULT 22 -"); diff --git a/migrations/004.php b/migrations/004.php deleted file mode 100644 index 5a47d2a..0000000 --- a/migrations/004.php +++ /dev/null @@ -1,159 +0,0 @@ -store_result()) { - $res->free(); - } - } while ($database->more_results() && $database->next_result()); -} - -$this->database->autocommit(FALSE); - -$result = $this->database->query(" - SELECT uid FROM user WHERE uid = 'keys-sync' -"); -if ($result) { - if($result->num_rows === 0) { - $result->close(); - $result = $this->database->multi_query(" - INSERT INTO entity SET type = 'user'; - INSERT INTO user SET entity_id = ( - SELECT LAST_INSERT_ID() - ), uid = 'keys-sync', name = 'Synchronization script', email = '', auth_realm = 'local', admin = 1; - "); - free_results_004($this->database); - } else { - $result->close(); - $this->database->query(" - UPDATE user SET auth_realm = 'local', active = 1 WHERE uid = 'keys-sync'; - "); - } -} - - -$this->database->multi_query(" -CREATE TABLE `entity_event_2` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `entity_id` int(10) unsigned NOT NULL, - `actor_id` int(10) unsigned, - `date` datetime NOT NULL, - `details` mediumtext NOT NULL, - PRIMARY KEY (`id`), - KEY `FK_entity_event_entity_id` (`entity_id`), - KEY `FK_entity_event_actor_id` (`actor_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -INSERT entity_event_2 SELECT * FROM entity_event; - -DROP TABLE entity_event; -RENAME TABLE entity_event_2 TO entity_event; - -ALTER TABLE `entity_event` - ADD CONSTRAINT `FK_entity_event_actor_id` FOREIGN KEY (`actor_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, - ADD CONSTRAINT `FK_entity_event_entity_id` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE; -"); -free_results_004($this->database); - - -$this->database->multi_query(" -CREATE TABLE `group_event_2` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `group` int(10) unsigned NOT NULL, - `entity_id` int(10) unsigned, - `date` datetime NOT NULL, - `details` mediumtext NOT NULL, - PRIMARY KEY (`id`), - KEY `FK_group_event_group` (`group`), - KEY `FK_group_event_entity` (`entity_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT; - -INSERT group_event_2 SELECT * FROM group_event; - -DROP TABLE group_event; -RENAME TABLE group_event_2 TO group_event; - -ALTER TABLE `group_event` - ADD CONSTRAINT `FK_group_event_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, - ADD CONSTRAINT `FK_group_event_group` FOREIGN KEY (`group`) REFERENCES `group` (`entity_id`) ON DELETE CASCADE; -"); -free_results_004($this->database); - - -$this->database->multi_query(" -CREATE TABLE `group_member_2` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `group` int(10) unsigned NOT NULL, - `entity_id` int(10) unsigned NOT NULL, - `add_date` datetime NOT NULL, - `added_by` int(10) unsigned, - PRIMARY KEY (`id`), - UNIQUE KEY `group_entity_id` (`group`, `entity_id`), - KEY `FK_group_member_entity` (`entity_id`), - KEY `FK_group_member_entity_2` (`added_by`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT; - -INSERT group_member_2 SELECT * FROM group_member; - -DROP TABLE group_member; -RENAME TABLE group_member_2 TO group_member; - -ALTER TABLE `group_member` - ADD CONSTRAINT `FK_group_member_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE, - ADD CONSTRAINT `FK_group_member_entity_2` FOREIGN KEY (`added_by`) REFERENCES `entity` (`id`) ON DELETE SET NULL, - ADD CONSTRAINT `FK_group_member_group` FOREIGN KEY (`group`) REFERENCES `group` (`entity_id`) ON DELETE CASCADE -"); -free_results_004($this->database); - - -$this->database->multi_query(" -CREATE TABLE `server_event_2` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `server_id` int(10) unsigned NOT NULL, - `actor_id` int(10) unsigned, - `date` datetime NOT NULL, - `details` mediumtext NOT NULL, - PRIMARY KEY (`id`), - KEY `FK_server_log_server` (`server_id`), - KEY `FK_server_event_actor_id` (`actor_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -INSERT server_event_2 SELECT * FROM server_event; - -DROP TABLE server_event; -RENAME TABLE server_event_2 TO server_event; - -ALTER TABLE `server_event` - ADD CONSTRAINT `FK_server_event_actor_id` FOREIGN KEY (`actor_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, - ADD CONSTRAINT `FK_server_log_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE; -"); -free_results_004($this->database); - - -$this->database->multi_query(" -CREATE TABLE `server_note_2` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `server_id` int(10) unsigned NOT NULL, - `entity_id` int(10) unsigned, - `date` datetime NOT NULL, - `note` mediumtext NOT NULL, - PRIMARY KEY (`id`), - KEY `FK_server_note_server` (`server_id`), - KEY `FK_server_note_user` (`entity_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -INSERT server_note_2 SELECT * FROM server_note; - -DROP TABLE server_note; -RENAME TABLE server_note_2 TO server_note; - -ALTER TABLE `server_note` - ADD CONSTRAINT `FK_server_note_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, - ADD CONSTRAINT `FK_server_note_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE -"); -free_results_004($this->database); - -$this->database->commit(); - -$this->database->autocommit(TRUE); diff --git a/migrations/005.php b/migrations/005.php deleted file mode 100644 index beb76bd..0000000 --- a/migrations/005.php +++ /dev/null @@ -1,21 +0,0 @@ -database->query(" -UPDATE `public_key` SET `fingerprint_sha256` = null where `fingerprint_sha256` IN ( - SELECT `fingerprint_sha256` FROM `public_key` GROUP BY `fingerprint_sha256` HAVING COUNT(*) > 1 -) -"); - -$this->database->query(" -ALTER TABLE `public_key` ADD CONSTRAINT `public_key_fingerprint` UNIQUE (`fingerprint_sha256`) -"); - -$this->database->query(" -ALTER TABLE `public_key` ADD COLUMN `upload_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -"); - -$this->database->query(" -ALTER TABLE `public_key` ADD COLUMN `active` BOOLEAN NOT NULL DEFAULT TRUE -"); diff --git a/migrations/006.php b/migrations/006.php deleted file mode 100644 index 12a71d2..0000000 --- a/migrations/006.php +++ /dev/null @@ -1,56 +0,0 @@ -store_result()) { - $res->free(); - } - } while ($database->more_results() && $database->next_result()); -} - -$this->database->autocommit(FALSE); - -$this->database->multi_query(" -CREATE TABLE `access_option_2` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `access_id` int(10) unsigned NOT NULL, - `option` enum('command', 'from', 'environment', 'no-agent-forwarding', 'no-port-forwarding', 'no-pty', 'no-X11-forwarding', 'no-user-rc') NOT NULL, - `value` text, - PRIMARY KEY (`id`), - UNIQUE KEY `access_id_option` (`access_id`, `option`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -INSERT access_option_2 SELECT * FROM access_option; - -DROP TABLE access_option; -RENAME TABLE access_option_2 TO access_option; - -ALTER TABLE `access_option` -ADD CONSTRAINT `FK_access_option_access` FOREIGN KEY (`access_id`) REFERENCES `access` (`id`) ON DELETE CASCADE -"); -free_results_006($this->database); - -$this->database->multi_query(" -CREATE TABLE `server_ldap_access_option_2` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `server_id` int(10) unsigned NOT NULL, - `option` enum('command', 'from', 'environment', 'no-agent-forwarding', 'no-port-forwarding', 'no-pty', 'no-X11-forwarding', 'no-user-rc') NOT NULL, - `value` text, - PRIMARY KEY (`id`), - UNIQUE KEY `server_id_option` (`server_id`, `option`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -INSERT server_ldap_access_option_2 SELECT * FROM server_ldap_access_option; - -DROP TABLE server_ldap_access_option; -RENAME TABLE server_ldap_access_option_2 TO server_ldap_access_option; - -ALTER TABLE `server_ldap_access_option` -ADD CONSTRAINT `FK_server_ldap_access_option_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE -"); -free_results_006($this->database); - -$this->database->commit(); - -$this->database->autocommit(TRUE); diff --git a/model/access.php b/model/access.php deleted file mode 100644 index bdb6ed0..0000000 --- a/model/access.php +++ /dev/null @@ -1,79 +0,0 @@ -id)) throw new BadMethodCallException('Access rule must be in directory before options can be added'); - $stmt = $this->database->prepare("INSERT INTO access_option SET access_id = ?, `option` = ?, value = ?"); - $stmt->bind_param('dss', $this->id, $option->option, $option->value); - $stmt->execute(); - $stmt->close(); - } - - /** - * Remove an SSH option from the access rule - * @param AccessOption $option to be removed - */ - public function delete_option(AccessOption $option) { - if(is_null($this->id)) throw new BadMethodCallException('Access rule must be in directory before options can be deleted'); - $stmt = $this->database->prepare("DELETE FROM access_option WHERE access_id = ? AND `option` = ?"); - $stmt->bind_param('ds', $this->id, $option->option); - $stmt->execute(); - $stmt->close(); - } - - /** - * Replace the current list of SSH access options with the provided array of options. - * This is a crude implementation - just deletes all existing options and adds new ones, with - * table locking for a small measure of safety. - * @param array $options array of AccessOption objects - */ - public function update_options(array $options) { - $stmt = $this->database->query("LOCK TABLES access_option WRITE"); - $oldoptions = $this->list_options(); - foreach($oldoptions as $oldoption) { - $this->delete_option($oldoption); - } - foreach($options as $option) { - $this->add_option($option); - } - $stmt = $this->database->query("UNLOCK TABLES"); - $this->dest_entity->sync_access(); - } - - /** - * List all current SSH access options applied to the access rule. - * @return array of AccessOption objects - */ - public function list_options() { - if(is_null($this->id)) throw new BadMethodCallException('Access rule must be in directory before options can be listed'); - $stmt = $this->database->prepare(" - SELECT * - FROM access_option - WHERE access_id = ? - ORDER BY `option` - "); - $stmt->bind_param('d', $this->id); - $stmt->execute(); - $result = $stmt->get_result(); - $options = array(); - while($row = $result->fetch_assoc()) { - $options[$row['option']] = new AccessOption($row['option'], $row); - } - $stmt->close(); - return $options; - } -} - -class AccessNotFoundException extends Exception {} diff --git a/model/accessoption.php b/model/accessoption.php deleted file mode 100644 index 0be1d3a..0000000 --- a/model/accessoption.php +++ /dev/null @@ -1,10 +0,0 @@ -id)) throw new BadMethodCallException('Entity must be in directory before log entries can be added'); - switch(get_class($this)) { - case 'User': - $scope = "user:{$this->uid}"; - break; - case 'ServerAccount': - $scope = "account:{$this->name}@{$this->server->hostname}"; - break; - case 'Group': - $scope = "group:{$this->name}"; - break; - default: - throw new BadMethodCallException('Unsupported entity type: '.get_class($this)); - } - $json = json_encode($details, JSON_UNESCAPED_UNICODE); - $stmt = $this->database->prepare("INSERT INTO entity_event SET entity_id = ?, actor_id = ?, date = UTC_TIMESTAMP(), details = ?"); - $stmt->bind_param('dds', $this->id, $this->active_user->entity_id, $json); - $stmt->execute(); - $stmt->close(); - - $text = "KeysScope=\"{$scope}\" KeysRequester=\"{$this->active_user->uid}\""; - foreach($details as $key => $value) { - $text .= ' Keys'.ucfirst($key).'="'.str_replace('"', '', $value).'"'; - } - openlog('keys', LOG_ODELAY, LOG_AUTH); - syslog($level, $text); - closelog(); - } - - /** - * Add the specified user as an administrator of the entity. - * Logging is performed by the inheriting classes. - * @param User $user to add as administrator - */ - public function add_admin(User $user) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Entity must be in directory before admins can be added'); - if(is_null($user->entity_id)) throw new InvalidArgumentException('User must be in directory before it can be made admin'); - $entity_id = $user->entity_id; - try { - $stmt = $this->database->prepare("INSERT INTO entity_admin SET entity_id = ?, admin = ?"); - $stmt->bind_param('dd', $this->entity_id, $entity_id); - $stmt->execute(); - $stmt->close(); - } catch(mysqli_sql_exception $e) { - if($e->getCode() == 1062) { - // Duplicate entry - ignore - } else { - throw $e; - } - } - } - - /** - * Remove the specified user as an administrator of the entity. - * @param User $user to remove as administrator - */ - public function delete_admin(User $user) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Entity must be in directory before admins can be deleted'); - if(is_null($user->entity_id)) throw new InvalidArgumentException('User must be in directory before it can be removed as admin'); - $entity_id = $user->entity_id; - $stmt = $this->database->prepare("DELETE FROM entity_admin WHERE entity_id = ? AND admin = ?"); - $stmt->bind_param('dd', $this->entity_id, $entity_id); - $stmt->execute(); - $stmt->close(); - } - - /** - * List all administrators of this entity. - * @return array of User objects - */ - public function list_admins() { - if(is_null($this->entity_id)) throw new BadMethodCallException('Entity must be in directory before admins can be listed'); - $stmt = $this->database->prepare("SELECT admin FROM entity_admin WHERE entity_id = ?"); - $stmt->bind_param('d', $this->entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - $admins = array(); - while($row = $result->fetch_assoc()) { - $admins[] = new User($row['admin']); - } - $stmt->close(); - return $admins; - } - - /** - * Add a public key to this entity for use with any outbound access rules that apply to it. - * Emailing and logging is handled by the inheriting classes. - * @param PublicKey $key to be added - */ - public function add_public_key(PublicKey $key) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Entity must be in directory before public keys can be added'); - $key->get_openssh_info(); - $key_type = $key->type; - $key_keydata = $key->keydata; - $key_comment = $key->comment; - $key_size = $key->keysize; - $key_fingerprint_md5 = $key->fingerprint_md5; - $key_fingerprint_sha256 = $key->fingerprint_sha256; - $key_randomart_md5 = $key->randomart_md5; - $key_randomart_sha256 = $key->randomart_sha256; - try { - $stmt = $this->database->prepare(" - INSERT INTO public_key SET - entity_id = ?, - type = ?, - keydata = ?, - comment = ?, - keysize = ?, - fingerprint_md5 = ?, - fingerprint_sha256 = ?, - randomart_md5 = ?, - randomart_sha256 = ? - "); - $stmt->bind_param('dsssdssss', $this->entity_id, $key_type, $key_keydata, $key_comment, $key_size, $key_fingerprint_md5, $key_fingerprint_sha256, $key_randomart_md5, $key_randomart_sha256); - $stmt->execute(); - $key->id = $stmt->insert_id; - $stmt->close(); - $this->sync_remote_access(); - } catch(mysqli_sql_exception $e) { - if($e->getCode() == 1062) { - // Duplicate entry - throw new PublicKeyAlreadyKnownException("public key already known"); - } else { - throw $e; - } - } - } - - /** - * Delete the specified public key from this entity. - * @param PublicKey $key to be removed - */ - public function delete_public_key(PublicKey $key) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Entity must be in directory before public keys can be deleted'); - $stmt = $this->database->prepare("UPDATE public_key SET active = false WHERE entity_id = ? AND id = ?"); - $stmt->bind_param('dd', $this->entity_id, $key->id); - $stmt->execute(); - $stmt->close(); - $this->sync_remote_access(); - } - - /** - * Retrieve a specific public key for this entity by its ID. - * @param int $id of public key to retrieve - * @return PublicKey matching the ID - * @throws PublicKeyNotFoundException if no public key exists with that ID - */ - public function get_public_key_by_id($id) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Entity must be in directory before public keys can be listed'); - $stmt = $this->database->prepare("SELECT * FROM public_key WHERE entity_id = ? AND id = ? AND active = true"); - $stmt->bind_param('dd', $this->entity_id, $id); - $stmt->execute(); - $result = $stmt->get_result(); - if($row = $result->fetch_assoc()) { - $key = new PublicKey($row['id'], $row); - } else { - throw new PublicKeyNotFoundException('Public key does not exist.'); - } - $stmt->close(); - return $key; - } - - /** - * List all public keys associated with this entity, optionally filtered by account name and hostname - * for any of the keys that have destination rules applied. - * @todo this is perhaps an unintuitive place to do this kind of filtering - * @param string|null $account_name to filter for in the destination rules for each key - * @param string|null $hostname to filter for in the destination rules for each key - * @return array of PublicKey objects - */ - public function list_public_keys($account_name = null, $hostname = null) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Entity must be in directory before public keys can be listed'); - $stmt = $this->database->prepare(" - SELECT public_key.*, COUNT(public_key_dest_rule.id) AS dest_rule_count - FROM public_key - LEFT JOIN public_key_dest_rule ON public_key_dest_rule.public_key_id = public_key.id - WHERE entity_id = ? AND active = true - GROUP BY public_key.id - "); - $stmt->bind_param('d', $this->entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - $keys = array(); - while($row = $result->fetch_assoc()) { - if((is_null($account_name) && is_null($hostname)) || $row['dest_rule_count'] == 0) { - $include = true; - } else { - $include = false; - $rulestmt = $this->database->prepare("SELECT * FROM public_key_dest_rule WHERE public_key_id = ?"); - $rulestmt->bind_param('d', $row['id']); - $rulestmt->execute(); - $ruleresult = $rulestmt->get_result(); - if($ruleresult->num_rows == 0) { - // Key has no destination rules defined, include it everywhere - $include = true; - } else { - // Apply destination rules - while($rule = $ruleresult->fetch_assoc()) { - $filter1 = '/^'.str_replace('\*', '.*', preg_quote($rule['account_name_filter'], '/')).'$/i'; - $filter2 = '/^'.str_replace('\*', '.*', preg_quote($rule['hostname_filter'], '/')).'$/i'; - if(preg_match($filter1, $account_name) && preg_match($filter2, $hostname)) { - $include = true; - break; - } - } - } - } - if($include) { - $keys[] = new PublicKey($row['id'], $row); - } - } - $stmt->close(); - return $keys; - } - - /** - * Retrieve a specific access rule towards this entity by its ID (inbound access). - * @param int $id to retrieve - * @return Access object - * @throws AccessNotFoundException if no access rule exists with this ID - */ - public function get_access_by_id($id) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Entity must be in directory before access can be listed'); - $stmt = $this->database->prepare(" - SELECT access.*, entity.type - FROM access - INNER JOIN entity ON entity.id = access.source_entity_id - WHERE access.dest_entity_id = ? AND access.id = ? - "); - $stmt->bind_param('dd', $this->entity_id, $id); - $stmt->execute(); - $result = $stmt->get_result(); - if($row = $result->fetch_assoc()) { - switch($row['type']) { - case 'user': $source_entity = new User($row['source_entity_id']); break; - case 'server account': $source_entity = new ServerAccount($row['source_entity_id']); break; - case 'group': $source_entity = new Group($row['source_entity_id']); break; - } - $row['granted_by'] = new User($row['granted_by']); - $row['source_entity'] = $source_entity; - $row['dest_entity'] = $this; - $access = new Access($row['id'], $row); - } else { - throw new AccessNotFoundException('Access rule does not exist.'); - } - $stmt->close(); - return $access; - } - - /** - * List all access rules that grant access to this entity (inbound access). - * @return array of Access objects - */ - public function list_access() { - if(is_null($this->entity_id)) throw new BadMethodCallException('Entity must be in directory before access can be listed'); - $stmt = $this->database->prepare(" - SELECT access.*, entity.type - FROM access - INNER JOIN entity ON entity.id = access.source_entity_id - LEFT JOIN user ON user.entity_id = entity.id - LEFT JOIN server_account ON server_account.entity_id = entity.id - LEFT JOIN server ON server.id = server_account.server_id - LEFT JOIN `group` ON `group`.entity_id = entity.id - WHERE dest_entity_id = ? - ORDER BY entity.type, user.uid, server.hostname, server_account.name, `group`.name - "); - $stmt->bind_param('d', $this->entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - $access_list = array(); - while($row = $result->fetch_assoc()) { - switch($row['type']) { - case 'user': $source_entity = new User($row['source_entity_id']); break; - case 'server account': $source_entity = new ServerAccount($row['source_entity_id']); break; - case 'group': $source_entity = new Group($row['source_entity_id']); break; - } - $row['granted_by'] = new User($row['granted_by']); - $row['source_entity'] = $source_entity; - $access_list[] = new Access($row['id'], $row); - } - $stmt->close(); - return $access_list; - } - - /** - * List all requests for access to this entity (inbound access). - * @return array of AccessRequest objects - */ - public function list_access_requests() { - if(is_null($this->entity_id)) throw new BadMethodCallException('Entity must be in directory before access can be listed'); - $stmt = $this->database->prepare(" - SELECT access_request.*, entity.type - FROM access_request - INNER JOIN entity ON entity.id = access_request.source_entity_id - LEFT JOIN user ON user.entity_id = entity.id - LEFT JOIN server_account ON server_account.entity_id = entity.id - LEFT JOIN server ON server.id = server_account.server_id - LEFT JOIN `group` ON `group`.entity_id = entity.id - WHERE dest_entity_id = ? - ORDER BY entity.type, user.uid, server.hostname, server_account.name, `group`.name - "); - $stmt->bind_param('d', $this->entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - $access_requests = array(); - while($row = $result->fetch_assoc()) { - switch($row['type']) { - case 'user': $source_entity = new User($row['source_entity_id']); break; - case 'server account': $source_entity = new ServerAccount($row['source_entity_id']); break; - case 'group': $source_entity = new Group($row['source_entity_id']); break; - } - $row['requested_by'] = new User($row['requested_by']); - $row['source_entity'] = $source_entity; - $access_requests[] = new AccessRequest($row['id'], $row); - } - $stmt->close(); - return $access_requests; - } - - /** - * List all access rules that grant this entity access to other entities (outbound access). - * @return array of Access objects - */ - public function list_remote_access() { - if(is_null($this->entity_id)) throw new BadMethodCallException('Entity must be in directory before remote access can be listed'); - $stmt = $this->database->prepare(" - SELECT access.*, entity.type - FROM access - INNER JOIN entity ON access.dest_entity_id = entity.id - LEFT JOIN user ON user.entity_id = entity.id - LEFT JOIN server_account ON server_account.entity_id = entity.id - LEFT JOIN server ON server.id = server_account.server_id - LEFT JOIN `group` ON `group`.entity_id = entity.id - WHERE access.source_entity_id = ? - ORDER BY entity.type, user.uid, server.hostname, server_account.name, `group`.name - "); - $stmt->bind_param('d', $this->entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - $access_list = array(); - while($row = $result->fetch_assoc()) { - switch($row['type']) { - case 'user': $dest_entity = new User($row['dest_entity_id']); break; - case 'server account': $dest_entity = new ServerAccount($row['dest_entity_id']); break; - case 'group': $dest_entity = new Group($row['dest_entity_id']); break; - } - $row['granted_by'] = new User($row['granted_by']); - $row['dest_entity'] = $dest_entity; - $access_list[] = new Access($row['id'], $row); - } - $stmt->close(); - return $access_list; - } - - /** - * Trigger a sync for this entity - must be implemented by inheriting class. - */ - abstract public function sync_access(); - - /** - * Trigger a sync for all entities that this entity has access to (and recurse to group members). - * @param $seen used to prevent infinite recursion and double-syncing by tracking all entities seen so far - */ - public function sync_remote_access(&$seen = array()) { - $seen[$this->entity_id] = true; - // Sync whatever this entity has access to - $access_list = $this->list_remote_access(); - foreach($access_list as $access) { - $access->dest_entity->sync_access(); - } - // Sync whatever groups this entity is a member of - global $group_dir; - $memberships = $group_dir->list_group_membership($this); - foreach($memberships as $group) { - if(!isset($seen[$group->entity_id])) { - $group->sync_remote_access($seen); - } - } - // If this is a user, also sync across LDAP-based servers - global $server_dir; - global $sync_request_dir; - if(get_class($this) == 'User') { - $servers = $server_dir->list_servers(array(), array('authorization' => array('manual LDAP', 'automatic LDAP'))); - foreach($servers as $server) { - $sync_request = new SyncRequest; - $sync_request->server_id = $server->id; - $sync_request->account_name = $this->uid; - $sync_request_dir->add_sync_request($sync_request); - } - } - } -} -class PublicKeyAlreadyKnownException extends Exception {} diff --git a/model/entityevent.php b/model/entityevent.php deleted file mode 100644 index 88cb407..0000000 --- a/model/entityevent.php +++ /dev/null @@ -1,27 +0,0 @@ -data['actor_id']); - return $actor; - default: - return parent::__get($field); - } - } -} diff --git a/model/eventdirectory.php b/model/eventdirectory.php index fc7e2e2..8cb361f 100644 --- a/model/eventdirectory.php +++ b/model/eventdirectory.php @@ -60,10 +60,6 @@ public function list_events($include = array(), $filter = array(), $limit = 100) $events[] = new ServerEvent($row['id'], $row); } elseif($row['event_type'] == 'user') { $events[] = new UserEvent($row['id'], $row); - } elseif($row['event_type'] == 'server account') { - $events[] = new ServerAccountEvent($row['id'], $row); - } elseif($row['event_type'] == 'group') { - $events[] = new GroupEvent($row['id'], $row); } } $stmt->close(); diff --git a/model/group.php b/model/group.php deleted file mode 100644 index 3d29758..0000000 --- a/model/group.php +++ /dev/null @@ -1,332 +0,0 @@ -data['system'])) $this->data['system'] = 0; - } - - /** - * Write property changes to database and log the changes. - * Triggers a resync if the group was activated/deactivated. - */ - public function update() { - if($this->data['system']) $this->data['active'] = 1; // Cannot disable system groups - $changes = parent::update(); - $resync = false; - foreach($changes as $change) { - $loglevel = LOG_INFO; - switch($change->field) { - case 'active': - $resync = true; - if($change->new_value == 1) $loglevel = LOG_WARNING; - break; - } - $this->log(array('action' => 'Setting update', 'value' => $change->new_value, 'oldvalue' => $change->old_value, 'field' => ucfirst(str_replace('_', ' ', $change->field))), $loglevel); - } - if($resync) { - $this->sync_access(); - $this->sync_remote_access(); - } - } - - /** - * List all log events for this group. - * @return array of GroupEvent objects - */ - public function get_log() { - if(is_null($this->id)) throw new BadMethodCallException('Group must be in directory before log entries can be listed'); - $stmt = $this->database->prepare("SELECT * FROM entity_event WHERE entity_id = ? ORDER BY id DESC"); - $stmt->bind_param('d', $this->id); - $stmt->execute(); - $result = $stmt->get_result(); - $log = array(); - while($row = $result->fetch_assoc()) { - $log[] = new GroupEvent($row['id'], $row); - } - $stmt->close(); - return $log; - } - - /** - * Add the specified user as an administrator of the group. - * This action is logged with a warning level as it is increasing an access level. - * @param User $user to add as administrator - */ - public function add_admin(User $user) { - global $config; - parent::add_admin($user); - $url = $config['web']['baseurl'].'/groups/'.urlencode($this->name); - $email = new Email; - $email->subject = "Administrator for {$this->name} group"; - $email->add_cc($config['email']['report_address'], $config['email']['report_name']); - $email->add_recipient($user->email, $user->name); - $email->body = "{$this->active_user->name} ({$this->active_user->uid}) has added you as an administrator for the '{$this->name}' group. You can administer this group from <$url>"; - $email->send(); - $this->log(array('action' => 'Administrator add', 'value' => "user:{$user->uid}"), LOG_WARNING); - } - - /** - * Remove the specified user as an administrator of the group. - * This action is logged with a warning level as it means the removed user will no longer - * receive notifications for any changes done to this group. - * @param User $user to remove as administrator - */ - public function delete_admin(User $user) { - parent::delete_admin($user); - $this->log(array('action' => 'Administrator remove', 'value' => "user:{$user->uid}"), LOG_WARNING); - } - - - /** - * Add the specified entity (User/ServerAccount/Group†) as a member of the group. - * †Adding a Group as a member of a group (nested groups) is no longer allowed by the UI. - * This action is logged with a warning level as it is potentially granting access. - * @todo remove nested group functionality - * @param Entity $entity to add as a group member - */ - public function add_member(Entity $entity) { - global $config; - if(is_null($this->entity_id)) throw new BadMethodCallException('Group must be in directory before members can be added'); - if(is_null($entity->entity_id)) throw new InvalidArgumentException('Entity must be in directory before it can be added to a group'); - $entity_id = $entity->entity_id; - switch(get_class($entity)) { - case 'User': - $name = "user {$entity->uid}"; - $mailsubject = "{$entity->uid} added to {$this->name} group by {$this->active_user->uid}"; - $mailbody = "{$entity->name} ({$entity->uid}) has been added to the {$this->name} group by {$this->active_user->name} ({$this->active_user->uid})."; - $logmsg = array('action' => 'Member add', 'value' => "user:{$entity->uid}"); - break; - case 'ServerAccount': - // We should not allow adding server accounts to a group if the active user is not an admin of that server or server account - if(!$this->active_user->admin && !$this->active_user->admin_of($entity->server) && !$this->active_user->admin_of($entity)) { - throw new InvalidArgumentException('Active user is not an administrator of the specified server account'); - } - $name = "account {$entity->name}@{$entity->server->hostname}"; - $mailsubject = "{$entity->name}@{$entity->server->hostname} added to {$this->name} group by {$this->active_user->uid}"; - $mailbody = "{$entity->name}@{$entity->server->hostname} has been added to the {$this->name} group by {$this->active_user->name} ({$this->active_user->uid})."; - $logmsg = array('action' => 'Member add', 'value' => "account:{$entity->name}@{$entity->server->hostname}"); - break; - case 'Group': - // We should not allow adding groups to a group if the active user is not an admin of that group - if(!$this->active_user->admin && !$this->active_user->admin_of($entity)) { - throw new InvalidArgumentException('Active user is not an administrator of the specified group'); - } - $name = "group {$entity->name}"; - $mailsubject = "{$entity->name} group added to {$this->name} group by {$this->active_user->uid}"; - $mailbody = "The {$entity->name} group has been added to the {$this->name} group by {$this->active_user->name} ({$this->active_user->uid})."; - $logmsg = array('action' => 'Member add', 'value' => "group:{$entity->name}"); - break; - } - try { - $stmt = $this->database->prepare("INSERT INTO group_member SET `group` = ?, entity_id = ?, add_date = UTC_TIMESTAMP(), added_by = ?"); - $stmt->bind_param('ddd', $this->entity_id, $entity_id, $this->active_user->entity_id); - $stmt->execute(); - $stmt->close(); - $this->log($logmsg, LOG_WARNING); - if($this->active_user->uid != 'import-script') { - $email = new Email; - foreach($this->list_admins() as $admin) { - $email->add_recipient($admin->email, $admin->name); - } - $email->add_cc($config['email']['report_address'], $config['email']['report_name']); - $email->subject = $mailsubject; - $email->body = $mailbody; - $email->send(); - } - } catch(mysqli_sql_exception $e) { - if($e->getCode() == 1062) { - // Duplicate entry - ignore - } else { - throw $e; - } - } - $entity->sync_access(); // This entity is now a member of the group, so any access rules that apply to the group now apply to the entity - $this->sync_remote_access(); // If this group has access to anything, this entity now also has access to it - } - - /** - * Remove the specified entity (User/ServerAccount/Group) as a member of the group. - * @todo remove nested group functionality - * @param Entity $entity to remove as a group member - */ - public function delete_member(Entity $entity) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Group must be in directory before members can be deleted'); - switch(get_class($entity)) { - case 'User': - $this->log(array('action' => 'Member remove', 'value' => "user:{$entity->uid}")); - break; - case 'ServerAccount': - $this->log(array('action' => 'Member remove', 'value' => "account:{$entity->name}@{$entity->server->hostname}")); - break; - case 'Group': - $this->log(array('action' => 'Member remove', 'value' => "group:{$entity->name}")); - break; - } - $stmt = $this->database->prepare("DELETE FROM group_member WHERE `group` = ? AND entity_id = ?"); - $stmt->bind_param('ds', $this->entity_id, $entity->entity_id); - $stmt->execute(); - $stmt->close(); - // Resync both the entity being removed and the group itself - $entity->sync_access(); - $this->sync_remote_access(); - } - - /** - * List all members of the group. - * @todo remove nested group functionality - * @return array of User/ServerAccount/Group objects - */ - public function list_members() { - if(is_null($this->entity_id)) throw new BadMethodCallException('Group must be in directory before members can be listed'); - $stmt = $this->database->prepare(" - SELECT entity.id, entity.type, add_date, added_by - FROM group_member - INNER JOIN entity ON group_member.entity_id = entity.id - LEFT JOIN user ON user.entity_id = entity.id - LEFT JOIN server_account ON server_account.entity_id = entity.id - LEFT JOIN server ON server.id = server_account.server_id - LEFT JOIN `group` ON `group`.entity_id = entity.id - WHERE group_member.group = ? - ORDER BY entity.type, user.uid, server.hostname, server_account.name, `group`.name - "); - $stmt->bind_param('d', $this->entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - $members = array(); - while($row = $result->fetch_assoc()) { - $row['added_by'] = new User($row['added_by']); - switch($row['type']) { - case 'user': $members[] = new User($row['id'], $row); break; - case 'server account': $members[] = new ServerAccount($row['id'], $row); break; - case 'group': $members[] = new Group($row['id'], $row); break; - } - } - $stmt->close(); - return $members; - } - - /** - * Grant the specified entity (User/ServerAccount/Group) access to members of this group. - * An email is sent to the group admins and sec-ops to inform them of the change. - * This action is logged with a warning level as it is granting access. - * @param Entity $entity to add as a group member - * @param array $access_options array of AccessOption rules to apply to the granted access - */ - public function add_access(Entity $entity, array $access_options) { - global $config; - if(is_null($this->entity_id)) throw new BadMethodCallException('Group must be in directory before access can be added'); - if(is_null($entity->entity_id)) throw new InvalidArgumentException('Entity must be in directory before it can be granted access to a group'); - $access = new Access; - $access->dest_entity_id = $this->entity_id; - $access->source_entity_id = $entity->entity_id; - $access->granted_by = $this->active_user->entity_id; - try { - $stmt = $this->database->prepare("INSERT INTO access SET dest_entity_id = ?, source_entity_id = ?, grant_date = UTC_TIMESTAMP(), granted_by = ?"); - $stmt->bind_param('ddd', $access->dest_entity_id, $access->source_entity_id, $access->granted_by); - $stmt->execute(); - $access->id = $stmt->insert_id; - $stmt->close(); - switch(get_class($entity)) { - case 'User': - $this->log(array('action' => 'Access add', 'value' => "user:{$entity->uid}"), LOG_WARNING); - $mailsubject = "{$entity->uid} granted access to {$this->name} group resources by {$this->active_user->uid}"; - $mailbody = "{$entity->name} ({$entity->uid}) has been granted access to resources in the {$this->name} group by {$this->active_user->name} ({$this->active_user->uid})."; - break; - case 'ServerAccount': - $this->log(array('action' => 'Access add', 'value' => "account:{$entity->name}@{$entity->server->hostname}"), LOG_WARNING); - $mailsubject = "{$entity->name}@{$entity->server->hostname} granted access to {$this->name} group resources by {$this->active_user->uid}"; - $mailbody = "{$entity->name}@{$entity->server->hostname} has been granted access to resources in the {$this->name} group by {$this->active_user->name} ({$this->active_user->uid})."; - break; - case 'Group': - $this->log(array('action' => 'Access add', 'value' => "group:{$entity->name}"), LOG_WARNING); - $mailsubject = "{$entity->name} group granted access to {$this->name} group resources by {$this->active_user->uid}"; - $mailbody = "The {$entity->name} group has been granted access to resources in the {$this->name} group by {$this->active_user->name} ({$this->active_user->uid})."; - break; - } - if($this->active_user->uid != 'import-script') { - $email = new Email; - foreach($this->list_admins() as $admin) { - $email->add_recipient($admin->email, $admin->name); - } - $email->add_cc($config['email']['report_address'], $config['email']['report_name']); - $email->subject = $mailsubject; - $email->body = $mailbody; - $email->send(); - } - foreach($access_options as $access_option) { - $access->add_option($access_option); - } - } catch(mysqli_sql_exception $e) { - if($e->getCode() == 1062) { - // Duplicate entry - ignore - } else { - throw $e; - } - } - $this->sync_access(); - } - - /** - * Revoke the specified access rule to members of this group. - * @param Access $access rule to be removed - */ - public function delete_access(Access $access) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Group must be in directory before access can be deleted'); - $entity = $access->source_entity; - switch(get_class($entity)) { - case 'User': - $this->log(array('action' => 'Access remove', 'value' => "user:{$entity->uid}")); - break; - case 'ServerAccount': - $this->log(array('action' => 'Access remove', 'value' => "account:{$entity->name}@{$entity->server->hostname}")); - break; - case 'Group': - $this->log(array('action' => 'Access remove', 'value' => "group:{$entity->name}")); - break; - } - $stmt = $this->database->prepare("DELETE FROM access WHERE dest_entity_id = ? AND id = ?"); - $stmt->bind_param('ds', $this->entity_id, $access->id); - $stmt->execute(); - $stmt->close(); - $this->sync_access(); - } - - /** - * List all groups that *this* group is a member of, searched recursively. - * Note: nested groups are no longer allowed by the UI. - * @todo remove nested group functionality - * @return array of Group objects - */ - public function list_group_membership() { - global $group_dir; - return $group_dir->list_group_membership($this); - } - - /** - * Trigger a resync for all members of this group, searched recursively†. - * †Nested groups are no longer allowed by the UI. - * @todo remove nested group functionality - * @param array $seen keep track of entities we've already processed to prevent infinite recursion - */ - public function sync_access(&$seen = array()) { - $seen[$this->entity_id] = true; - $members = $this->list_members(); - foreach($members as $entity) { - if(!isset($seen[$entity->entity_id])) { - $entity->sync_access($seen); - } - } - } -} diff --git a/model/groupdirectory.php b/model/groupdirectory.php deleted file mode 100644 index b8ffc47..0000000 --- a/model/groupdirectory.php +++ /dev/null @@ -1,188 +0,0 @@ -name; - $system = $group->system; - $this->database->begin_transaction(); - $stmt = $this->database->prepare("INSERT INTO entity SET type = 'group'"); - $stmt->execute(); - $group->entity_id = $stmt->insert_id; - $stmt->close(); - $stmt = $this->database->prepare("INSERT INTO `group` SET entity_id = ?, name = ?, system = ?"); - $stmt->bind_param('dsd', $group->entity_id, $name, $system); - try { - $stmt->execute(); - $stmt->close(); - $this->database->commit(); - $group->log(array('action' => 'Group add')); - } catch(mysqli_sql_exception $e) { - $this->database->rollback(); - if($e->getCode() == 1062) { - // Duplicate entry - throw new GroupAlreadyExistsException("Group {$group->name} already exists"); - } else { - throw $e; - } - } - } - - /** - * Get a group from the database by its entity ID. - * @param int $entity_id of group - * @return Group with specified entity ID - * @throws GroupNotFoundException if no group with that entity ID exists - */ - public function get_group_by_id($entity_id) { - $stmt = $this->database->prepare("SELECT * FROM `group` WHERE entity_id = ?"); - $stmt->bind_param('d', $entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - if($row = $result->fetch_assoc()) { - $group = new Group($row['entity_id'], $row); - } else { - throw new GroupNotFoundException('Group does not exist.'); - } - $stmt->close(); - return $group; - } - - /** - * Get a group from the database by its name. - * @param string $name of group - * @return Group with specified name - * @throws GroupNotFoundException if no group with that name exists - */ - public function get_group_by_name($name) { - $stmt = $this->database->prepare("SELECT * FROM `group` WHERE name = ?"); - $stmt->bind_param('s', $name); - $stmt->execute(); - $result = $stmt->get_result(); - if($row = $result->fetch_assoc()) { - $group = new Group($row['entity_id'], $row); - } else { - throw new GroupNotFoundException('Group does not exist'); - } - $stmt->close(); - return $group; - } - - /** - * List all groups in the database. - * @param array $include list of extra data to include in response - * @param array $filter list of field/value pairs to filter results on - * @return array of Group objects - */ - public function list_groups($include = array(), $filter = array()) { - // WARNING: The search query is not parameterized - be sure to properly escape all input - $fields = array("`group`.*"); - $joins = array(); - $where = array(); - foreach($filter as $field => $value) { - if($value) { - switch($field) { - case 'name': - $where[] = "`group`.name REGEXP '".$this->database->escape_string($value)."'"; - break; - case 'active': - $where[] = "`group`.active IN (".implode(", ", array_map('intval', $value)).")"; - break; - case 'admin': - $where[] = "admin_filter.admin = ".intval($value); - $joins['adminfilter'] = "INNER JOIN entity_admin admin_filter ON admin_filter.entity_id = `group`.entity_id"; - break; - case 'member': - $where[] = "member_filter.entity_id = ".intval($value); - $joins['memberfilter'] = "INNER JOIN group_member member_filter ON member_filter.group = `group`.entity_id"; - break; - } - } - } - foreach($include as $inc) { - switch($inc) { - case 'admins': - $fields[] = "GROUP_CONCAT(DISTINCT user.uid SEPARATOR ', ') AS admins"; - $joins['admins'] = "LEFT JOIN entity_admin ON entity_admin.entity_id = `group`.entity_id"; - $joins['adminusers'] = "LEFT JOIN user ON user.entity_id = entity_admin.admin AND user.active"; - break; - case 'members': - $fields[] = "COUNT(DISTINCT group_member.entity_id) AS member_count"; - $joins['members'] = "LEFT JOIN group_member ON group_member.group = `group`.entity_id"; - break; - } - } - try { - $stmt = $this->database->prepare(" - SELECT ".implode(", ", $fields)." - FROM `group` ".implode(" ", $joins)." - ".(count($where) == 0 ? "" : "WHERE (".implode(") AND (", $where).")")." - GROUP BY group.entity_id - ORDER BY `group`.name - "); - } catch(mysqli_sql_exception $e) { - if($e->getCode() == 1139) { - throw new GroupSearchInvalidRegexpException; - } else { - throw $e; - } - } - $stmt->execute(); - $result = $stmt->get_result(); - $groups = array(); - while($row = $result->fetch_assoc()) { - $groups[] = new Group($row['entity_id'], $row); - } - $stmt->close(); - return $groups; - } - - /** - * List all groups that the given entity (User/ServerAccount/Group†) is a member of (searched recursively†). - * †Nested groups are no longer allowed by the UI. - * @todo remove nested group functionality - * @param Entity $entity to find in group memberships - * @param array $via keep track of groups we have already searched through to prevent infinite recursion† - * @param array $groups to allow the function to add to the list of groups when recursing† - * @return array of Group objects - */ - public function list_group_membership(Entity $entity, $via = array(), &$groups = array()) { - $stmt = $this->database->prepare(" - SELECT `group`.*, add_date, added_by - FROM group_member - INNER JOIN `group` ON `group`.entity_id = group_member.group - WHERE group_member.entity_id = ? - ORDER BY `group`.name - "); - $stmt->bind_param('d', $entity->entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - while($row = $result->fetch_assoc()) { - $row['added_by'] = new User($row['added_by']); - $group = new Group($row['entity_id'], $row); - $groups[] = $group; - $skip = false; - foreach($via as $check) { - if($group->id == $check->id) $skip = true; - } - if(!$skip) { - $thisvia = $via; - $thisvia[] = $group; - $this->list_group_membership($group, $thisvia, $groups); - } - } - $stmt->close(); - return $groups; - } -} - -class GroupNotFoundException extends Exception {} -class GroupAlreadyExistsException extends Exception {} -class GroupNotDeletableException extends Exception {} -class GroupSearchInvalidRegexpException extends Exception {} \ No newline at end of file diff --git a/model/groupevent.php b/model/groupevent.php deleted file mode 100644 index 817595e..0000000 --- a/model/groupevent.php +++ /dev/null @@ -1,20 +0,0 @@ -data['entity_id']); - return $group; - default: - return parent::__get($field); - } - } -} diff --git a/model/migrationdirectory.php b/model/migrationdirectory.php index b360bc2..895b79d 100644 --- a/model/migrationdirectory.php +++ b/model/migrationdirectory.php @@ -6,7 +6,7 @@ class MigrationDirectory extends DBDirectory { /** * Increment this constant to activate a new migration from the migrations directory */ - const LAST_MIGRATION = 6; + const LAST_MIGRATION = 2; public function __construct() { parent::__construct(); diff --git a/model/publickey.php b/model/publickey.php deleted file mode 100644 index 49676bb..0000000 --- a/model/publickey.php +++ /dev/null @@ -1,275 +0,0 @@ -type = $matches[1]; - $this->keydata = $matches[2]; - if(isset($matches[3])) { - $this->comment = $matches[3]; - } elseif(is_null($uid)) { - $this->comment = date('Y-m-d'); - } else { - $this->comment = $uid.'-'.date('Y-m-d'); - } - $algorithm = $this->get_openssh_info(); - $hash_md5 = md5(base64_decode($this->keydata)); - $hash_sha256 = hash('sha256', base64_decode($this->keydata), true); - $this->fingerprint_md5 = rtrim(chunk_split($hash_md5, 2, ':'), ':'); - $this->fingerprint_sha256 = rtrim(base64_encode($hash_sha256), '='); - $this->randomart_md5 = $this->generate_randomart($hash_md5, "{$algorithm} {$this->keysize}", 'MD5'); - $this->randomart_sha256 = $this->generate_randomart(bin2hex($hash_sha256), "{$algorithm} {$this->keysize}", 'SHA256'); - $this->upload_date = date('Y-m-d H:i:s', time()); - - if($this->keysize < $minbits && !$force) { - throw new InvalidArgumentException("Insufficient bits in public key"); - } - } - - /** - * Determine the algorithm and keysize of a key by passing it to OpenSSH's ssh-keygen utility. - * @return string algorithm in use - */ - public function get_openssh_info() { - $filename = tempnam('/tmp', 'key-test-'); - $file = fopen($filename, 'w'); - fwrite($file, $this->export()); - fclose($file); - exec('/usr/bin/ssh-keygen -lf '.escapeshellarg($filename).' 2>/dev/null', $output); - unlink($filename); - if(count($output) == 1 && preg_match('|^([0-9]+) .* \(([A-Z0-9]+)\)$|', $output[0], $matches)) { - $this->keysize = intval($matches[1]); - return $matches[2]; - } else { - throw new InvalidArgumentException("Public key doesn't look valid"); - } - } - - /** - * Generate random art for the key in the same way that OpenSSH does - * OpenSSH random art uses the 'drunken bishop' algorithm as explained at - * https://pthree.org/2013/05/30/openssh-keys-and-the-drunken-bishop/ - * @param string $string key hash to generate randomart of - * @param string $keytype string containing text to include at the top of the randomart - * @param string $algo string containing text to include at the bottom of the randomart - * @return string containing generated randomart - */ - function generate_randomart($string, $keytype, $algo) { - // Basic constants - $max_x = 16; // Map size, x dimension - $max_y = 8; // Map size, y dimension - $s_x = 8; // Starting position, x coord - $s_y = 4; // Starting position, y coord - - // Character mapping - $char_map = array(' ', '.', 'o', '+', '=', '*', 'B', 'O', 'X', '@', '%', '&', '#', '/', '^'); - - // Build empty map - $map = array(); - for($x = 0; $x <= $max_x; $x++) { - $map[$x] = array(); - for($y = 0; $y <= $max_y; $y++) { - $map[$x][$y] = 0; - } - } - - // Set the bishop to his starting position - $b_x = $s_x; // Bishop position, x coord - $b_y = $s_y; // Bishop position, y coord - - // Let him wander - $chunks = str_split($string, 2); - foreach($chunks as $chunk) { - $binary = str_pad(base_convert($chunk, 16, 2), 8, '0', STR_PAD_LEFT); - foreach(array_reverse(str_split($binary, 2)) as $bit_pair) { - // Work out which diagonal direction he will move based on the bit pair - $dx = ($bit_pair[1] == 0 ? -1 : 1); - $dy = ($bit_pair[0] == 0 ? -1 : 1); - $b_x += $dx; - $b_y += $dy; - - // Stop him wandering outside the map - $b_x = min(max($b_x, 0), 16); - $b_y = min(max($b_y, 0), 8); - - // Increment count at his new position - $map[$b_x][$b_y]++; - } - } - - // Output his path within the map - $output = "+".str_pad('['.$keytype.']', $max_x + 1, '-', STR_PAD_BOTH)."+\n"; - for($y = 0; $y <= $max_y; $y++) { - $output .= "|"; - for($x = 0; $x <= $max_x; $x++) { - if($x == $b_x && $y == $b_y) { - // End position - $output .= 'E'; - } elseif($x == $s_x && $y == $s_y) { - // Start position - $output .= 'S'; - } else { - // Output character corresponding to number of passes - if(isset($char_map[$map[$x][$y]])) { - $output .= $char_map[$map[$x][$y]]; - } else { - $output .= '^'; - } - } - } - $output .= "|\n"; - } - $output .= "+".str_pad('['.$algo.']', $max_x + 1, '-', STR_PAD_BOTH)."+"; - return $output; - } - - /** - * Provide the key in OpenSSH-text-format. - * @return string key in OpenSSH-text-format - */ - public function export() { - return "{$this->type} {$this->keydata} {$this->comment}"; - } - - /** - * Provide a text summary of details about the key, including hashes, randomart and link to view it. - * @return string text summary - */ - public function summarize_key_information() { - global $config; - $url = $config['web']['baseurl'].'/pubkeys/'.urlencode($this->id); - $output = "The key fingerprint is:\n"; - $output .= " MD5:{$this->fingerprint_md5}\n"; - $output .= " SHA256:{$this->fingerprint_sha256}\n\n"; - $output .= "The key randomart is:\n"; - $randomart_md5 = explode("\n", $this->randomart_md5); - $randomart_sha256 = explode("\n", $this->randomart_sha256); - foreach($randomart_md5 as $ref => $line) { - $output .= $line.' '.$randomart_sha256[$ref]."\n"; - } - $output .= "\nYou can also view the key at <$url>"; - return $output; - } - - /** - * Add a GPG signature for this public key. - * @param PublicKeySignature $sig GPG signature to add - */ - public function add_signature(PublicKeySignature $sig) { - if(is_null($this->id)) throw new BadMethodCallException('Public key must be in directory before signatures can be added'); - $sig->validate(); - $stmt = $this->database->prepare("INSERT INTO public_key_signature SET public_key_id = ?, signature = ?, upload_date = UTC_TIMESTAMP(), fingerprint = ?, sign_date = ?"); - $stmt->bind_param('dsss', $this->id, $sig->signature, $sig->fingerprint, $sig->sign_date); - $stmt->execute(); - $sig->id = $stmt->insert_id; - $stmt->close(); - $this->owner->sync_remote_access(); - } - - /** - * Delete a GPG signature for this public key. - * @param PublicKeySignature $sig GPG signature to remove - */ - public function delete_signature(PublicKeySignature $sig) { - if(is_null($this->id)) throw new BadMethodCallException('Public key must be in directory before signatures can be deleted'); - $stmt = $this->database->prepare("DELETE FROM public_key_signature WHERE public_key_id = ? AND id = ?"); - $stmt->bind_param('dd', $this->id, $sig->id); - $stmt->execute(); - $stmt->close(); - $this->owner->sync_remote_access(); - } - - /** - * List all GPG signatures stored for this public key. - * @return array of PublicKeySignature objects - */ - public function list_signatures() { - if(is_null($this->entity_id)) throw new BadMethodCallException('Public key must be in directory before signatures can be listed'); - $stmt = $this->database->prepare("SELECT * FROM public_key_signature WHERE public_key_id = ?"); - $stmt->bind_param('d', $this->id); - $stmt->execute(); - $result = $stmt->get_result(); - $sigs = array(); - while($row = $result->fetch_assoc()) { - $sig = new PublicKeySignature($row['id'], $row); - $sig->public_key = $this; - $sigs[] = $sig; - } - $stmt->close(); - return $sigs; - } - - /** - * Add a destination rule specifying where this key is allowed to be synced to. - * @param PublicKeyDestRule $rule destination rule to be added - */ - public function add_destination_rule(PublicKeyDestRule $rule) { - if(is_null($this->id)) throw new BadMethodCallException('Public key must be in directory before destination rules can be added'); - $stmt = $this->database->prepare("INSERT INTO public_key_dest_rule SET public_key_id = ?, account_name_filter = ?, hostname_filter = ?"); - $stmt->bind_param('dss', $this->id, $rule->account_name_filter, $rule->hostname_filter); - $stmt->execute(); - $rule->id = $stmt->insert_id; - $stmt->close(); - $this->owner->sync_remote_access(); - } - - /** - * Delete a destination rule that specified where this key was allowed to be synced to. - * @param PublicKeyDestRule $rule destination rule to be removed - */ - public function delete_destination_rule(PublicKeyDestRule $rule) { - if(is_null($this->id)) throw new BadMethodCallException('Public key must be in directory before destination rules can be added'); - $stmt = $this->database->prepare("DELETE FROM public_key_dest_rule WHERE public_key_id = ? AND id = ?"); - $stmt->bind_param('dd', $this->id, $rule->id); - $stmt->execute(); - $stmt->close(); - $this->owner->sync_remote_access(); - } - - /** - * List all destination rule currently applying to this key. - * @return array of PublicKeyDestRule objects - */ - public function list_destination_rules() { - if(is_null($this->entity_id)) throw new BadMethodCallException('Public key must be in directory before destination rules can be listed'); - $stmt = $this->database->prepare("SELECT * FROM public_key_dest_rule WHERE public_key_id = ?"); - $stmt->bind_param('d', $this->id); - $stmt->execute(); - $result = $stmt->get_result(); - $rules = array(); - while($row = $result->fetch_assoc()) { - $rules[] = new PublicKeyDestRule($row['id'], $row); - } - $stmt->close(); - return $rules; - } -} diff --git a/model/publickeydestrule.php b/model/publickeydestrule.php deleted file mode 100644 index 2b9fe14..0000000 --- a/model/publickeydestrule.php +++ /dev/null @@ -1,13 +0,0 @@ -database->prepare(" - SELECT public_key.*, entity.type AS entity_type - FROM public_key - INNER JOIN entity ON entity.id = public_key.entity_id - WHERE public_key.id = ? AND active = true - "); - $stmt->bind_param('d', $id); - $stmt->execute(); - $result = $stmt->get_result(); - if($row = $result->fetch_assoc()) { - switch($row['entity_type']) { - case 'user': $row['owner'] = new User($row['entity_id']); break; - case 'server account': $row['owner'] = new ServerAccount($row['entity_id']); break; - } - $key = new PublicKey($row['id'], $row); - } else { - throw new PublicKeyNotFoundException('Public key does not exist.'); - } - $stmt->close(); - return $key; - } - - /** - * List stored public keys, optionally filtered by various parameters. - * See also Entity::list_public_keys function for retrieving keys belonging to a specific entity. - * @param array $include list of extra data to include in response - currently unused - * @param array $filter list of field/value pairs to filter results on - * @return array of PublicKey objects - */ - public function list_public_keys($include = array(), $filter = array()) { - // WARNING: The search query is not parameterized - be sure to properly escape all input - $fields = array("public_key.*, entity.type AS entity_type"); - $joins = array(); - $where = array(); - $where[] = "public_key.active = true"; - foreach($filter as $field => $value) { - if($value) { - switch($field) { - case 'type': - $where[] = "public_key.type = '".$this->database->escape_string($value)."'"; - break; - case 'keysize-min': - $where[] = "public_key.keysize >= ".intval($this->database->escape_string($value)); - break; - case 'keysize-max': - $where[] = "public_key.keysize <= ".intval($this->database->escape_string($value)); - break; - case 'fingerprint': - $where[] = "public_key.fingerprint_md5 = '".$this->database->escape_string($value)."' OR public_key.fingerprint_sha256 = '".$this->database->escape_string($value)."'"; - break; - } - } - } - $stmt = $this->database->prepare(" - SELECT ".implode(", ", $fields)." - FROM public_key ".implode(" ", $joins)." - INNER JOIN entity ON entity.id = public_key.entity_id - LEFT JOIN user ON user.entity_id = entity.id - LEFT JOIN server_account ON server_account.entity_id = entity.id - LEFT JOIN server ON server.id = server_account.server_id - ".(count($where) == 0 ? "" : "WHERE (".implode(") AND (", $where).")")." - ORDER BY entity.type, user.uid, server.hostname, server_account.name - "); - $stmt->execute(); - $result = $stmt->get_result(); - $pubkeys = array(); - while($row = $result->fetch_assoc()) { - switch($row['entity_type']) { - case 'user': $row['owner'] = new User($row['entity_id']); break; - case 'server account': $row['owner'] = new ServerAccount($row['entity_id']); break; - } - $pubkeys[] = new PublicKey($row['id'], $row); - } - return $pubkeys; - } -} - -class PublicKeyNotFoundException extends Exception {} diff --git a/model/publickeysignature.php b/model/publickeysignature.php deleted file mode 100644 index 4918691..0000000 --- a/model/publickeysignature.php +++ /dev/null @@ -1,33 +0,0 @@ -verify($this->public_key->export().$line_ending, $this->signature); - if(is_array($info)) { - $sig = reset($info); - if($sig['validity'] > 0) break; - } else { - throw new InvalidArgumentException("Signature doesn't seem valid"); - } - } - if($sig['validity'] == 0) { - #throw new InvalidArgumentException("Signature doesn't validate against pubkey"); - } - $this->fingerprint = $sig['fingerprint']; - $this->sign_date = gmdate('Y-m-d H:i:s', $sig['timestamp']); - } -} diff --git a/model/server.php b/model/server.php index 2ac577e..3289edc 100644 --- a/model/server.php +++ b/model/server.php @@ -81,7 +81,7 @@ public function get_log() { /** * List all log events for this server and any accounts on the server. - * @return array of ServerEvent/ServerAccountEvent objects + * @return array of ServerEvent objects */ public function get_log_including_accounts() { if(is_null($this->id)) throw new BadMethodCallException('Server must be in directory before log entries can be listed'); @@ -105,8 +105,6 @@ public function get_log_including_accounts() { while($row = $result->fetch_assoc()) { if($row['type'] == 'server') { $log[] = new ServerEvent($row['id'], $row); - } elseif($row['type'] == 'server account') { - $log[] = new ServerAccountEvent($row['id'], $row); } } $stmt->close(); @@ -133,321 +131,6 @@ public function get_last_sync_event() { return $event; } - /** - * Add the specified user or group as an administrator of the server. - * This action is logged with a warning level as it is increasing an access level. - * @param Entity $entity user or group to add as administrator - */ - public function add_admin(Entity $entity) { - global $config; - if(is_null($this->id)) throw new BadMethodCallException('Server must be in directory before admins can be added'); - if(is_null($entity->entity_id)) throw new InvalidArgumentException('User or group must be in directory before it can be made admin'); - $entity_id = $entity->entity_id; - try { - $url = $config['web']['baseurl'].'/servers/'.urlencode($this->hostname); - $email = new Email; - $email->subject = "Administrator for {$this->hostname}"; - $email->add_cc($config['email']['report_address'], $config['email']['report_name']); - switch(get_class($entity)) { - case 'User': - $email->add_recipient($entity->email, $entity->name); - $email->body = "{$this->active_user->name} ({$this->active_user->uid}) has added you as a server administrator for {$this->hostname}. You can administer access to this server from <$url>"; - $logmsg = array('action' => 'Administrator add', 'value' => "user:{$entity->uid}"); - break; - case 'Group': - foreach($entity->list_members() as $member) { - if(get_class($member) == 'User') { - $email->add_recipient($member->email, $member->name); - } - } - $email->body = "{$this->active_user->name} ({$this->active_user->uid}) has added the {$entity->name} group as server administrator for {$this->hostname}. You are a member of the {$entity->name} group, so you can administer access to this server from <$url>"; - $logmsg = array('action' => 'Administrator add', 'value' => "group:{$entity->name}"); - break; - default: - throw new InvalidArgumentException('Entities of type '.get_class($entity).' cannot be added as server admins'); - } - $stmt = $this->database->prepare("INSERT INTO server_admin SET server_id = ?, entity_id = ?"); - $stmt->bind_param('dd', $this->id, $entity_id); - $stmt->execute(); - $stmt->close(); - if($this->active_user->uid != 'import-script') { - $this->log($logmsg, LOG_WARNING); - $email->send(); - } - } catch(mysqli_sql_exception $e) { - if($e->getCode() == 1062) { - // Duplicate entry - ignore - } else { - throw $e; - } - } - } - - /** - * Remove the specified user or group as an administrator of the server. - * This action is logged with a warning level as it means the removed user/group will no longer - * receive notifications for any changes done to this server. - * @param Entity $entity user or group to remove as administrator - */ - public function delete_admin(Entity $entity) { - if(is_null($this->id)) throw new BadMethodCallException('Server must be in directory before admins can be deleted'); - if(is_null($entity->entity_id)) throw new InvalidArgumentException('User or group must be in directory before it can be removed as admin'); - $entity_id = $entity->entity_id; - switch(get_class($entity)) { - case 'User': - $this->log(array('action' => 'Administrator remove', 'value' => "user:{$entity->uid}"), LOG_WARNING); - break; - case 'Group': - $this->log(array('action' => 'Administrator remove', 'value' => "group:{$entity->name}"), LOG_WARNING); - break; - default: - throw new InvalidArgumentException('Entities of type '.get_class($entity).' should not exist as server admins'); - } - $stmt = $this->database->prepare("DELETE FROM server_admin WHERE server_id = ? AND entity_id = ?"); - $stmt->bind_param('dd', $this->id, $entity_id); - $stmt->execute(); - $stmt->close(); - } - - /** - * List all administrators of this server. - * @return array of User/Group objects - */ - public function list_admins() { - if(is_null($this->id)) throw new BadMethodCallException('Server must be in directory before admins can be listed'); - $stmt = $this->database->prepare("SELECT entity_id, type FROM server_admin INNER JOIN entity ON entity.id = server_admin.entity_id WHERE server_id = ?"); - $stmt->bind_param('d', $this->id); - $stmt->execute(); - $result = $stmt->get_result(); - $admins = array(); - while($row = $result->fetch_assoc()) { - if(strtolower($row['type']) == "user") { - $admins[] = new User($row['entity_id']); - } elseif(strtolower($row['type']) == "group") { - $admins[] = new Group($row['entity_id']); - } - } - $stmt->close(); - return $admins; - } - - /** - * Return the list of all users who can administrate this server, including - * via group membership of a group that has been made administrator. - * @return array of User objects - */ - public function list_effective_admins() { - $admins = $this->list_admins(); - $e_admins = array(); - foreach($admins as $admin) { - switch(get_class($admin)) { - case 'Group': - if($admin->active) { - $members = $admin->list_members(); - foreach($members as $member) { - if(get_class($member) == 'User') { - $e_admins[] = $member; - } - } - } - break; - case 'User': - $e_admins[] = $admin; - break; - } - } - return $e_admins; - } - - /** - * Create any standard accounts that should exist on every server, and add them to the related - * groups. - */ - public function add_standard_accounts() { - global $group_dir, $config; - if(!isset($config['defaults']['account_groups'])) return; - foreach($config['defaults']['account_groups'] as $account_name => $group_name) { - $account = new ServerAccount; - $account->name = $account_name; - $this->add_account($account); - try { - $group = $group_dir->get_group_by_name($group_name); - } catch(GroupNotFoundException $e) { - $group = new Group; - $group->name = $group_name; - $group->system = 1; - $group_dir->add_group($group); - } - $group->add_member($account); - } - } - - /** - * Create a new account on the server. - * Reactivates an existing account if one exists with the same name. - * @param ServerAccount $account to be added - * @throws AccountNameInvalid if account name is empty - */ - public function add_account(ServerAccount &$account) { - if(is_null($this->id)) throw new BadMethodCallException('Server must be in directory before accounts can be added'); - $account_name = $account->name; - if($account_name === '') throw new AccountNameInvalid('Account name cannot be empty'); - if(substr($account_name, 0, 1) === '.') throw new AccountNameInvalid('Account name cannot begin with .'); - $sync_status = is_null($account->sync_status) ? 'not synced yet' : $account->sync_status; - $this->database->begin_transaction(); - $stmt = $this->database->prepare("INSERT INTO entity SET type = 'server account'"); - $stmt->execute(); - $account->entity_id = $stmt->insert_id; - $stmt->close(); - $stmt = $this->database->prepare("INSERT INTO server_account SET entity_id = ?, server_id = ?, name = ?, sync_status = ?"); - $stmt->bind_param('ddss', $account->entity_id, $this->id, $account_name, $sync_status); - try { - $stmt->execute(); - $stmt->close(); - $this->database->commit(); - $this->log(array('action' => 'Account add', 'value' => $account_name)); - } catch(mysqli_sql_exception $e) { - $this->database->rollback(); - if($e->getCode() == 1062) { - // Duplicate entry - $account = $this->get_account_by_name($account_name); - $account->active = 1; - $account->update(); - } else { - throw $e; - } - } - } - - /** - * Get a server account from the database by its name. - * @param string $name of account - * @return ServerAccount with specified name - * @throws ServerAccountNotFoundException if no account with that name exists - */ - public function get_account_by_name($name) { - if(is_null($this->id)) throw new BadMethodCallException('Server must be in directory before accounts can be listed'); - $stmt = $this->database->prepare("SELECT entity_id, name FROM server_account WHERE server_id = ? AND name = ?"); - $stmt->bind_param('ds', $this->id, $name); - $stmt->execute(); - $result = $stmt->get_result(); - if($row = $result->fetch_assoc()) { - $account = new ServerAccount($row['entity_id'], $row); - } else { - throw new ServerAccountNotFoundException('Account does not exist.'); - } - $stmt->close(); - return $account; - } - - /** - * List accounts stored for this server. - * @param array $include list of extra data to include in response - currently unused - * @param array $filter list of field/value pairs to filter results on - * @return array of ServerAccount objects - */ - public function list_accounts($include = array(), $filter = array()) { - // WARNING: The search query is not parameterized - be sure to properly escape all input - if(is_null($this->id)) throw new BadMethodCallException('Server must be in directory before accounts can be listed'); - $where = array('server_id = '.intval($this->id), 'active = 1'); - $joins = array("LEFT JOIN access_request ON access_request.dest_entity_id = server_account.entity_id"); - foreach($filter as $field => $value) { - if($value) { - switch($field) { - case 'admin': - $where[] = "admin_filter.admin = ".intval($value); - $joins['adminfilter'] = "INNER JOIN entity_admin admin_filter ON admin_filter.entity_id = server_account.entity_id"; - break; - } - } - } - $stmt = $this->database->prepare(" - SELECT server_account.entity_id, name, - COUNT(DISTINCT access_request.source_entity_id) AS pending_requests - FROM server_account - ".implode("\n", $joins)." - WHERE (".implode(") AND (", $where).") - GROUP BY server_account.entity_id - ORDER BY name - "); - $stmt->execute(); - $result = $stmt->get_result(); - $accounts = array(); - while($row = $result->fetch_assoc()) { - $accounts[] = new ServerAccount($row['entity_id'], $row); - } - $stmt->close(); - return $accounts; - } - - /** - * Add an access option that should be applied to all LDAP accounts on the server. - * Access options include "command", "from", "no-port-forwarding" etc. - * @param ServerLDAPAccessOption $option to be added - */ - public function add_ldap_access_option(ServerLDAPAccessOption $option) { - if(is_null($this->id)) throw new BadMethodCallException('Server must be in directory before LDAP access options can be added'); - $stmt = $this->database->prepare("INSERT INTO server_ldap_access_option SET server_id = ?, `option` = ?, value = ?"); - $stmt->bind_param('dss', $this->id, $option->option, $option->value); - $stmt->execute(); - $stmt->close(); - } - - /** - * Remove an access option from all LDAP accounts on the server. - * Access options include "command", "from", "no-port-forwarding" etc. - * @param ServerLDAPAccessOption $option to be removed - */ - public function delete_ldap_access_option(ServerLDAPAccessOption $option) { - if(is_null($this->id)) throw new BadMethodCallException('Server must be in directory before LDAP access options can be deleted'); - $stmt = $this->database->prepare("DELETE FROM server_ldap_access_option WHERE server_id = ? AND `option` = ?"); - $stmt->bind_param('ds', $this->id, $option->option); - $stmt->execute(); - $stmt->close(); - } - - /** - * Replace the current list of LDAP access options with the provided array of options. - * This is a crude implementation - just deletes all existing options and adds new ones, with - * table locking for a small measure of safety. - * @param array $options array of ServerLDAPAccessOption objects - */ - public function update_ldap_access_options(array $options) { - $stmt = $this->database->query("LOCK TABLES server_ldap_access_option WRITE"); - $oldoptions = $this->list_ldap_access_options(); - foreach($oldoptions as $oldoption) { - $this->delete_ldap_access_option($oldoption); - } - foreach($options as $option) { - $this->add_ldap_access_option($option); - } - $stmt = $this->database->query("UNLOCK TABLES"); - $this->sync_access(); - } - - /** - * List all current LDAP access options applied to the server. - * @return array of ServerLDAPAccessOption objects - */ - public function list_ldap_access_options() { - if(is_null($this->id)) throw new BadMethodCallException('Server must be in directory before LDAP access options can be listed'); - $stmt = $this->database->prepare(" - SELECT * - FROM server_ldap_access_option - WHERE server_id = ? - ORDER BY `option` - "); - $stmt->bind_param('d', $this->id); - $stmt->execute(); - $result = $stmt->get_result(); - $options = array(); - while($row = $result->fetch_assoc()) { - $options[$row['option']] = new ServerLDAPAccessOption($row['option'], $row); - } - $stmt->close(); - return $options; - } - /** * Update the sync status for the server and write a log message if the status details have changed. * @param string $status "sync success", "sync failure" or "sync warning" diff --git a/model/serveraccount.php b/model/serveraccount.php deleted file mode 100644 index e8af954..0000000 --- a/model/serveraccount.php +++ /dev/null @@ -1,436 +0,0 @@ -server_id); - return $server; - default: - return parent::__get($field); - } - } - - /** - * Write property changes to database and log the changes. - * Triggers a resync of the server if account is activated/deactivated. - */ - public function update() { - global $config; - // Make it impossible to set default accounts to inactive - if(is_array($config['defaults']['account_groups'])) { - if(array_key_exists($this->data['name'], $config['defaults']['account_groups'])) { - $this->data['active'] = true; - } - } - $changes = parent::update(); - $resync = false; - foreach($changes as $change) { - $loglevel = LOG_INFO; - switch($change->field) { - case 'active': - if($this->sync_status != 'proposed') { - $resync = true; - } - if($change->new_value == 1) $loglevel = LOG_WARNING; - break; - } - $this->log(array('action' => 'Setting update', 'value' => $change->new_value, 'oldvalue' => $change->old_value, 'field' => ucfirst(str_replace('_', ' ', $change->field))), $loglevel); - } - if($resync) { - $this->server->sync_access(); - $this->sync_remote_access(); - } - } - - /** - * List all log events for this server account. - * @return array of ServerAccountEvent objects - */ - public function get_log() { - if(is_null($this->id)) throw new BadMethodCallException('Server account must be in directory before log entries can be listed'); - $stmt = $this->database->prepare(" - SELECT * - FROM entity_event - WHERE entity_id = ? - ORDER BY id DESC - "); - $stmt->bind_param('d', $this->id); - $stmt->execute(); - $result = $stmt->get_result(); - $log = array(); - while($row = $result->fetch_assoc()) { - $log[] = new ServerAccountEvent($row['id'], $row); - } - $stmt->close(); - return $log; - } - - /** - * Add the specified user as an administrator of the account. - * This action is logged with a warning level as it is increasing an access level. - * @param User $user to add as administrator - */ - public function add_admin(User $user) { - global $config; - parent::add_admin($user); - $url = $config['web']['baseurl'].'/servers/'.urlencode($this->server->hostname).'/accounts/'.urlencode($this->name); - $email = new Email; - $email->subject = "Administrator for {$this->name}@{$this->server->hostname}"; - $email->add_cc($config['email']['report_address'], $config['email']['report_name']); - $email->add_recipient($user->email, $user->name); - $email->body = "{$this->active_user->name} ({$this->active_user->uid}) has added you as an administrator for the '{$this->name}' account on {$this->server->hostname}. You can administer access to this account from <$url>"; - $email->send(); - $this->log(array('action' => 'Administrator add', 'value' => "user:{$user->uid}"), LOG_WARNING); - } - - /** - * Remove the specified user as an administrator of the account. - * This action is logged with a warning level as it means the removed user will no longer - * receive notifications for any changes done to this account. - * @param User $user to remove as administrator - */ - public function delete_admin(User $user) { - parent::delete_admin($user); - $this->log(array('action' => 'Administrator remove', 'value' => "user:{$user->uid}"), LOG_WARNING); - } - - /** - * Add a public key to this account for use with any outbound access rules that apply to it. - * An email is sent to the server admins and sec-ops to inform them of the change. - * This action is logged with a warning level as it is potentially granting SSH access with the key. - * @param PublicKey $key to be added - */ - public function add_public_key(PublicKey $key) { - global $config; - parent::add_public_key($key); - if($this->active_user->uid != 'import-script') { - $url = $config['web']['baseurl'].'/pubkeys/'.urlencode($key->id); - $email = new Email; - $email->add_reply_to($config['email']['admin_address'], $config['email']['admin_name']); - foreach($this->server->list_effective_admins() as $admin) { - $email->add_recipient($admin->email, $admin->name); - } - $email->add_cc($config['email']['report_address'], $config['email']['report_name']); - $email->subject = "A new SSH public key has been added to the account {$this->name}@{$this->server->hostname} by {$this->active_user->uid}"; - $email->body = "A new SSH public key has been added to the account {$this->name}@{$this->server->hostname} on SSH Key Authority. The key was added by {$this->active_user->name} ({$this->active_user->uid}).\n\nIf this key was added without your knowledge, please contact {$config['email']['admin_address']} immediately.\n\n".$key->summarize_key_information(); - $email->send(); - } - $this->log(array('action' => 'Pubkey add', 'value' => $key->fingerprint_md5), LOG_WARNING); - } - - /** - * Delete the specified public key from this account. - * @param PublicKey $key to be removed - */ - public function delete_public_key(PublicKey $key) { - parent::delete_public_key($key); - $this->log(array('action' => 'Pubkey remove', 'value' => $key->fingerprint_md5)); - } - - /** - * Request access for the specified entity (User/ServerAccount/Group) to this account. - * Stores the request and sends an email to the account admins and server admins notifying them of it. - * @param Entity $entity to request access for - */ - public function add_access_request(Entity $entity) { - global $config; - if(is_null($this->entity_id)) throw new BadMethodCallException('Server account must be added to server before access can be requested'); - try { - $request = new AccessRequest; - $request->dest_entity_id = $this->entity_id; - $request->source_entity_id = $entity->entity_id; - $request->requested_by = $this->active_user->entity_id; - $stmt = $this->database->prepare("INSERT INTO access_request SET dest_entity_id = ?, source_entity_id = ?, request_date = UTC_TIMESTAMP(), requested_by = ?"); - $stmt->bind_param('ddd', $request->dest_entity_id, $request->source_entity_id, $request->requested_by); - $stmt->execute(); - $request->id = $stmt->insert_id; - $stmt->close(); - switch(get_class($entity)) { - case 'User': - $this->log(array('action' => 'Access request', 'value' => "user:{$entity->uid}")); - break; - case 'ServerAccount': - $this->log(array('action' => 'Access request', 'value' => "account:{$entity->name}@{$entity->server->hostname}")); - break; - case 'Group': - $this->log(array('action' => 'Access request', 'value' => "group:{$entity->name}")); - break; - } - $account_admins = $this->list_admins(); - $server_admins = $this->server->list_effective_admins(); - if($this->active_user->uid != 'import-script') { - $email = new Email; - $email->add_reply_to($this->active_user->email, $this->active_user->name); - if(count($account_admins) == 0) { - foreach($server_admins as $admin) { - $email->add_recipient($admin->email, $admin->name); - } - } else { - foreach($account_admins as $admin) { - $email->add_recipient($admin->email, $admin->name); - } - foreach($server_admins as $admin) { - $email->add_cc($admin->email, $admin->name); - } - } - $url = $config['web']['baseurl'].'/servers/'.urlencode($this->server->hostname).'/accounts/'.urlencode($this->name); - switch(get_class($entity)) { - case 'User': - $email->subject = "{$entity->uid} requests access to {$this->name}@{$this->server->hostname}"; - $email->body = "{$entity->name} ({$entity->uid}) has requested access to {$this->name}@{$this->server->hostname}. View this request at <$url>"; - break; - case 'ServerAccount': - $email->subject = "{$this->active_user->uid} requests {$entity->name}@{$entity->server->hostname} access to {$this->name}@{$this->server->hostname}"; - $email->body = "{$this->active_user->name} ({$this->active_user->uid}) has requested that {$entity->name}@{$entity->server->hostname} have server-to-server access to {$this->name}@{$this->server->hostname}. View this request at <$url>"; - break; - case 'Group': - $email->subject = "{$this->active_user->uid} requests {$entity->name} group access to {$this->name}@{$this->server->hostname}"; - $email->body = "{$this->active_user->name} ({$this->active_user->uid}) has requested that the {$entity->name} group have access to {$this->name}@{$this->server->hostname}. View this request at <$url>"; - break; - } - $email->send(); - } - } catch(mysqli_sql_exception $e) { - if($e->getCode() == 1062) { - // Duplicate entry - ignore - } else { - throw $e; - } - } - } - - /** - * Approve a request for access to this account. - * For user access, sends an email to the requester informing them of the approval. - * Triggers add_access() and deletes the request from the DB. - * @todo send emails for all access types - * @param AccessRequest $request details - */ - public function approve_access_request(AccessRequest $request) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Server account must be added to server before access can be approved'); - $entity = $request->source_entity; - switch(get_class($entity)) { - case 'User': - $this->log(array('action' => 'Access approve', 'value' => "user:{$entity->uid}")); - $email = new Email; - $email->add_recipient($entity->email, $entity->name); - $email->subject = "Your request for access to {$this->name}@{$this->server->hostname} has been approved"; - $email->body = "You requested access to {$this->name}@{$this->server->hostname}, and this request has now been approved by {$this->active_user->name} ({$this->active_user->uid})."; - $email->send(); - break; - case 'ServerAccount': - $this->log(array('action' => 'Access approve', 'value' => "account:{$entity->name}@{$entity->server->hostname}")); - break; - case 'Group': - $this->log(array('action' => 'Access approve', 'value' => "group:{$entity->name}")); - break; - } - $options = array(); - $this->add_access($entity, $options); - $stmt = $this->database->prepare("DELETE FROM access_request WHERE dest_entity_id = ? AND id = ?"); - $stmt->bind_param('dd', $this->entity_id, $request->id); - $stmt->execute(); - $stmt->close(); - } - - /** - * Reject a request for access to this account. - * For user access, sends an email to the requester informing them of the rejection. - * Deletes the request from the DB. If the account was created as the result of a request and - * there are no other pending access requests for the account, deactivate the account. - * @todo send emails for all access types - * @param AccessRequest $request details - */ - public function reject_access_request(AccessRequest $request) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Server account must be added to server before access can be rejected'); - $entity = $request->source_entity; - switch(get_class($entity)) { - case 'User': - $this->log(array('action' => 'Access reject', 'value' => "user:{$entity->uid}")); - $email = new Email; - $email->add_recipient($entity->email, $entity->name); - $email->subject = "Your request for access to {$this->name}@{$this->server->hostname} has been rejected"; - $email->body = "You requested access to {$this->name}@{$this->server->hostname}, but this request has been rejected by {$this->active_user->name} ({$this->active_user->uid})."; - $email->send(); - break; - case 'ServerAccount': - $this->log(array('action' => 'Access reject', 'value' => "account:{$entity->name}@{$entity->server->hostname}")); - break; - case 'Group': - $this->log(array('action' => 'Access reject', 'value' => "group:{$entity->name}")); - break; - } - $stmt = $this->database->prepare("DELETE FROM access_request WHERE dest_entity_id = ? AND id = ?"); - $stmt->bind_param('dd', $this->entity_id, $request->id); - $stmt->execute(); - $stmt->close(); - if($this->sync_status == 'proposed') { - if(count($this->list_access_requests()) == 0) { - $this->active = 0; - $this->update(); - } - } - } - - /** - * Grant the specified entity (User/ServerAccount/Group) access to this server account. - * An email is sent to the account admins, server admins and sec-ops to inform them of the change. - * This action is logged with a warning level as it is granting access. - * @param Entity $entity to add as a group member - * @param array $access_options array of AccessOption rules to apply to the granted access - */ - public function add_access(Entity $entity, array $access_options) { - global $config; - if(is_null($this->entity_id)) throw new BadMethodCallException('Server account must be added to server before access can be added'); - if($this->sync_status == 'proposed') { - $this->sync_status = 'not synced yet'; - $this->update(); - } - try { - $access = new Access; - $access->dest_entity_id = $this->entity_id; - $access->source_entity_id = $entity->entity_id; - $access->granted_by = $this->active_user->entity_id; - $stmt = $this->database->prepare("INSERT INTO access SET dest_entity_id = ?, source_entity_id = ?, grant_date = UTC_TIMESTAMP(), granted_by = ?"); - $stmt->bind_param('ddd', $access->dest_entity_id, $access->source_entity_id, $access->granted_by); - $stmt->execute(); - $access->id = $stmt->insert_id; - $stmt->close(); - switch(get_class($entity)) { - case 'User': - $this->log(array('action' => 'Access add', 'value' => "user:{$entity->uid}"), LOG_WARNING); - $mailsubject = "Access granted for {$entity->uid} to {$this->name}@{$this->server->hostname} by {$this->active_user->uid}"; - $mailbody = "{$entity->name} ({$entity->uid}) has been granted access to {$this->name}@{$this->server->hostname} by {$this->active_user->name} ({$this->active_user->uid}). The changes will be synced to the server within a few seconds."; - break; - case 'ServerAccount': - $this->log(array('action' => 'Access add', 'value' => "account:{$entity->name}@{$entity->server->hostname}"), LOG_WARNING); - $mailsubject = "Access granted for {$entity->name}@{$entity->server->hostname} to {$this->name}@{$this->server->hostname} by {$this->active_user->uid}"; - $mailbody = "{$entity->name}@{$entity->server->hostname} has been granted server-to-server access to {$this->name}@{$this->server->hostname} by {$this->active_user->name} ({$this->active_user->uid}). The changes will be synced to the server within a few seconds."; - break; - case 'Group': - $this->log(array('action' => 'Access add', 'value' => "group:{$entity->name}"), LOG_WARNING); - $mailsubject = "Access granted for {$entity->name} group to {$this->name}@{$this->server->hostname} by {$this->active_user->uid}"; - $mailbody = "The {$entity->name} group has been granted access to {$this->name}@{$this->server->hostname} by {$this->active_user->name} ({$this->active_user->uid}). The changes will be synced to the server within a few seconds."; - break; - } - if($this->active_user->uid != 'import-script') { - $account_admins = $this->list_admins(); - $server_admins = $this->server->list_effective_admins(); - $email = new Email; - if(count($account_admins) == 0) { - foreach($server_admins as $admin) { - $email->add_recipient($admin->email, $admin->name); - } - } else { - foreach($account_admins as $admin) { - $email->add_recipient($admin->email, $admin->name); - } - foreach($server_admins as $admin) { - $email->add_cc($admin->email, $admin->name); - } - } - $email->add_cc($config['email']['report_address'], $config['email']['report_name']); - $email->subject = $mailsubject; - $email->body = $mailbody; - $email->send(); - } - foreach($access_options as $access_option) { - $access->add_option($access_option); - } - } catch(mysqli_sql_exception $e) { - if($e->getCode() == 1062) { - // Duplicate entry - ignore - } else { - throw $e; - } - } - $this->sync_access(); - } - - /** - * Revoke the specified access rule for this account. - * @param Access $access rule to be removed - */ - public function delete_access(Access $access) { - if(is_null($this->entity_id)) throw new BadMethodCallException('Server account must be added to server before access can be deleted'); - $entity = $access->source_entity; - switch(get_class($entity)) { - case 'User': - $this->log(array('action' => 'Access remove', 'value' => "user:{$entity->uid}")); - break; - case 'ServerAccount': - $this->log(array('action' => 'Access remove', 'value' => "account:{$entity->name}@{$entity->server->hostname}")); - break; - case 'Group': - $this->log(array('action' => 'Access remove', 'value' => "group:{$entity->name}")); - break; - } - $stmt = $this->database->prepare("DELETE FROM access WHERE dest_entity_id = ? AND id = ?"); - $stmt->bind_param('dd', $this->entity_id, $access->id); - $stmt->execute(); - $stmt->close(); - $this->sync_access(); - } - - /** - * List all groups that this account is a member of. - * @return array of Group objects - */ - public function list_group_membership() { - global $group_dir; - return $group_dir->list_group_membership($this); - } - - /** - * Trigger a sync for this account. - */ - public function sync_access() { - global $sync_request_dir; - $sync_request = new SyncRequest; - $sync_request->server_id = $this->server_id; - $sync_request->account_name = $this->name; - $sync_request_dir->add_sync_request($sync_request); - } - - /** - * Determine if a sync is currently pending for this account. - * @return boolean true if a sync is pending - */ - public function sync_is_pending() { - $stmt = $this->database->prepare("SELECT * FROM sync_request WHERE server_id = ? AND (account_name = ? OR account_name IS NULL) ORDER BY account_name"); - $stmt->bind_param('ds', $this->server_id, $this->name); - $stmt->execute(); - $result = $stmt->get_result(); - return $result->num_rows > 0; - } - - /** - * Update the sync status for the account. - * @param string $status "sync success", "sync failure" or "sync warning" - */ - public function sync_report($status) { - if(is_null($this->id)) throw new BadMethodCallException('Server account must be in directory before sync reporting can be done'); - if($this->sync_status != 'proposed') { - $this->sync_status = $status; - $this->update(); - } - } -} diff --git a/model/serveraccountdirectory.php b/model/serveraccountdirectory.php deleted file mode 100644 index 5da9817..0000000 --- a/model/serveraccountdirectory.php +++ /dev/null @@ -1,29 +0,0 @@ -database->prepare("SELECT * FROM server_account WHERE entity_id = ?"); - $stmt->bind_param('d', $entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - if($row = $result->fetch_assoc()) { - $account = new ServerAccount($row['entity_id'], $row); - } else { - throw new ServerAccountNotFoundException('Server account does not exist.'); - } - $stmt->close(); - return $account; - } -} - -class ServerAccountNotFoundException extends Exception {} -class ServerAccountNotDeletableException extends Exception {} diff --git a/model/serveraccountevent.php b/model/serveraccountevent.php deleted file mode 100644 index de216fd..0000000 --- a/model/serveraccountevent.php +++ /dev/null @@ -1,17 +0,0 @@ -data['entity_id']); - return $group; - default: - return parent::__get($field); - } - } -} diff --git a/model/user.php b/model/user.php index 585c2e0..f02edd9 100644 --- a/model/user.php +++ b/model/user.php @@ -2,7 +2,7 @@ /** * Class that represents a user of this system */ -class User extends Entity { +class User extends Record { /** * Defines the database table that this object is stored in */ @@ -10,7 +10,7 @@ class User extends Entity { /** * Defines the field that is the primary key of the table */ - protected $idfield = 'entity_id'; + protected $idfield = 'id'; /** * LDAP connection object */ @@ -22,6 +22,29 @@ public function __construct($id = null, $preload_data = array()) { $this->ldap = $ldap; } + /** + * Write event details to syslog and to user_event table. + * @param array $details event paramaters to be logged + * @param int $level syslog priority as defined in http://php.net/manual/en/function.syslog.php + */ + public function log($details, $level = LOG_INFO) { + if(is_null($this->id)) throw new BadMethodCallException('User must be in directory before log entries can be added'); + $scope = "user:{$this->uid}"; + $json = json_encode($details, JSON_UNESCAPED_UNICODE); + $stmt = $this->database->prepare("INSERT INTO user_event SET user_id = ?, actor_id = ?, date = UTC_TIMESTAMP(), details = ?"); + $stmt->bind_param('dds', $this->id, $this->active_user->id, $json); + $stmt->execute(); + $stmt->close(); + + $text = "KeysScope=\"{$scope}\" KeysRequester=\"{$this->active_user->uid}\""; + foreach($details as $key => $value) { + $text .= ' Keys'.ucfirst($key).'="'.str_replace('"', '', $value).'"'; + } + openlog('keys', LOG_ODELAY, LOG_AUTH); + syslog($level, $text); + closelog(); + } + /** * Write property changes to database and log the changes. * Triggers a resync if the user was activated/deactivated. @@ -40,8 +63,6 @@ public function update() { if($change->new_value == 1) $loglevel = LOG_WARNING; break; case 'csrf_token': - case 'superior_entity_id': - return; } $this->log(array('action' => 'Setting update', 'value' => $change->new_value, 'oldvalue' => $change->old_value, 'field' => ucfirst(str_replace('_', ' ', $change->field))), $loglevel); } @@ -54,36 +75,15 @@ public function update() { * Delete the given user. */ public function delete() { - if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before it can be removed'); - $stmt = $this->database->prepare("DELETE FROM entity WHERE id = ?"); - $stmt->bind_param('d', $this->entity_id); - $stmt->execute(); - $stmt->close(); - $stmt = $this->database->prepare("DELETE FROM user WHERE entity_id = ?"); - $stmt->bind_param('d', $this->entity_id); + if(is_null($this->id)) throw new BadMethodCallException('User must be in directory before it can be removed'); + $stmt = $this->database->prepare("DELETE FROM user WHERE id = ?"); + $stmt->bind_param('d', $this->id); $stmt->execute(); $stmt->close(); $this->sync_remote_access(); } - /** - * Magic getter method - if superior field requested, return User object of user's superior - * @param string $field to retrieve - * @return mixed data stored in field - */ - public function &__get($field) { - global $user_dir; - switch($field) { - case 'superior': - if(is_null($this->superior_entity_id)) $superior = null; - else $superior = new User($this->superior_entity_id); - return $superior; - default: - return parent::__get($field); - } - } - /** * List all events on entities and servers that this user has administrator access to * @param array $include list of extra data to include in response @@ -91,127 +91,8 @@ public function &__get($field) { */ public function list_events($include = array()) { global $event_dir; - if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before events can be listed'); - return $event_dir->list_events($include, array('admin' => $this->entity_id)); - } - - /** - * List all servers that are administrated by this user - * @param array $include list of extra data to include in response - * @return array of Server objects - */ - public function list_admined_servers($include = array()) { - global $server_dir; - if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before admined servers can be listed'); - return $server_dir->list_servers($include, array('admin' => $this->entity_id, 'key_management' => array('none', 'keys', 'other'))); - } - - /** - * List all groups that are administrated by this user - * @param array $include list of extra data to include in response - * @return array of Group objects - */ - public function list_admined_groups($include = array()) { - global $group_dir; - if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before admined group can be listed'); - $groups = $group_dir->list_groups($include, array('admin' => $this->entity_id)); - return $groups; - } - - /** - * List all groups that this user is a member of - * @param array $include list of extra data to include in response - * @return array of Group objects - */ - public function list_group_memberships($include = array()) { - global $group_dir; - if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before group memberships can be listed'); - $groups = $group_dir->list_groups($include, array('member' => $this->entity_id)); - return $groups; - } - - /** - * Determine if this user is an administrator of the specified entity or server. - * @param Record $record object to check for administration privileges - * @return bool true if user is an administrator of the object - * @throws InvalidArgumentException if a non-administratable Record is provided - */ - public function admin_of(Record $record) { - switch(get_class($record)) { - case 'Server': - $stmt = $this->database->prepare(" - SELECT entity_id - FROM group_member - WHERE (`group` IN ( - SELECT entity_id - FROM server_admin - WHERE server_id = ?) - AND entity_id = ?) - UNION (SELECT entity_id - FROM server_admin - WHERE server_id = ? - AND entity_id = ?)"); - $stmt->bind_param('dddd', $record->id, $this->entity_id, $record->id, $this->entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - return $result->num_rows >= 1; - break; - case 'Group': - case 'ServerAccount': - $stmt = $this->database->prepare("SELECT * FROM entity_admin WHERE admin = ? AND entity_id = ?"); - $stmt->bind_param('dd', $this->entity_id, $record->entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - return $result->num_rows >= 1; - break; - default: - throw new InvalidArgumentException('Records of type '.get_class($record).' cannot be administered'); - } - } - - /** - * Determine if this user is a member of the specified group - * @param Group $group to check membership of - * @return bool true if user is an member of the group - */ - public function member_of(Group $group) { - $stmt = $this->database->prepare("SELECT * FROM group_member WHERE entity_id = ? AND `group` = ?"); - $stmt->bind_param('dd', $this->entity_id, $group->entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - return $result->num_rows >= 1; - } - - /** - * Add a public key to this user for use with any outbound access rules that apply to them. - * An email is sent to the user and sec-ops to inform them of the change. - * This action is logged with a warning level as it is potentially granting SSH access with the key. - * @param PublicKey $key to be added - */ - public function add_public_key(PublicKey $key) { - global $active_user, $config; - parent::add_public_key($key); - if($active_user->uid != 'import-script') { - $url = $config['web']['baseurl'].'/pubkeys/'.urlencode($key->id); - $email = new Email; - $email->add_reply_to($config['email']['admin_address'], $config['email']['admin_name']); - $email->add_recipient($this->email, $this->name); - $email->add_cc($config['email']['report_address'], $config['email']['report_name']); - $email->subject = "A new SSH public key has been added to your account ({$this->uid})"; - $email->body = "A new SSH public key has been added to your account on SSH Key Authority.\n\nIf you added this key then all is well. If you do not recall adding this key, please contact {$config['email']['admin_address']} immediately.\n\n".$key->summarize_key_information(); - $email->send(); - } - $this->log(array('action' => 'Pubkey add', 'value' => $key->fingerprint_md5), LOG_WARNING); - } - - /** - * Delete the specified public key from this user. - * @param PublicKey $key to be removed - */ - public function delete_public_key(PublicKey $key) { - global $active_user; - parent::delete_public_key($key); - $this->log(array('action' => 'Pubkey remove', 'value' => $key->fingerprint_md5)); + if(is_null($this->id)) throw new BadMethodCallException('User must be in directory before events can be listed'); + return $event_dir->list_events($include, array('admin' => $this->id)); } /** @@ -219,8 +100,8 @@ public function delete_public_key(PublicKey $key) { * @param UserAlert $alert to be displayed */ public function add_alert(UserAlert $alert) { - if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before alerts can be added'); - $stmt = $this->database->prepare("INSERT INTO user_alert SET entity_id = ?, class = ?, content = ?, escaping = ?"); + if(is_null($this->id)) throw new BadMethodCallException('User must be in directory before alerts can be added'); + $stmt = $this->database->prepare("INSERT INTO user_alert SET user_id = ?, class = ?, content = ?, escaping = ?"); $stmt->bind_param('dssd', $this->entity_id, $alert->class, $alert->content, $alert->escaping); $stmt->execute(); $alert->id = $stmt->insert_id; @@ -232,9 +113,9 @@ public function add_alert(UserAlert $alert) { * @return array of UserAlert objects */ public function pop_alerts() { - if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before alerts can be listed'); - $stmt = $this->database->prepare("SELECT * FROM user_alert WHERE entity_id = ?"); - $stmt->bind_param('d', $this->entity_id); + if(is_null($this->id)) throw new BadMethodCallException('User must be in directory before alerts can be listed'); + $stmt = $this->database->prepare("SELECT * FROM user_alert WHERE user_id = ?"); + $stmt->bind_param('d', $this->id); $stmt->execute(); $result = $stmt->get_result(); $alerts = array(); @@ -250,20 +131,6 @@ public function pop_alerts() { return $alerts; } - /** - * Determine if this user has been granted access to the specified account. - * @param ServerAccount $account to check for access - * @return bool true if user has access to the account - */ - public function has_access(ServerAccount $account) { - if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before access can be checked'); - $stmt = $this->database->prepare("SELECT * FROM access WHERE source_entity_id = ? AND dest_entity_id = ?"); - $stmt->bind_param('dd', $this->entity_id, $account->entity_id); - $stmt->execute(); - $result = $stmt->get_result(); - return (bool)$result->fetch_assoc(); - } - /** * Return HTML containing the user's CSRF token for inclusion in a POST form. * Also includes a random string of the same length to help guard against http://breachattack.com/ @@ -278,7 +145,7 @@ public function get_csrf_field() { * @return string CSRF token */ public function get_csrf_token() { - if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before CSRF token can be generated'); + if(is_null($this->id)) throw new BadMethodCallException('User must be in directory before CSRF token can be generated'); if(!isset($this->data['csrf_token'])) { $this->data['csrf_token'] = hash("sha512", mt_rand(0, mt_getrandmax())); $this->update(); @@ -336,37 +203,6 @@ public function get_details_from_ldap() { } } - /** - * Retrieve the user's superior from LDAP. - * @throws UserNotFoundException if the user is not found in LDAP - */ - public function get_superior_from_ldap() { - global $user_dir, $config; - if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before superior employee can be looked up'); - if(!isset($config['ldap']['user_superior'])) { - throw new BadMethodCallException("Cannot retrieve user's superior if user_superior is not configured"); - } - $ldapusers = $this->ldap->search($config['ldap']['dn_user'], LDAP::escape($config['ldap']['user_id']).'='.LDAP::escape($this->uid), array($config['ldap']['user_superior'])); - if($ldapuser = reset($ldapusers)) { - $superior = null; - if(isset($ldapuser[strtolower($config['ldap']['user_superior'])]) && $ldapuser[strtolower($config['ldap']['user_superior'])] != $this->uid) { - $superior_uid = $ldapuser[strtolower($config['ldap']['user_superior'])]; - try { - $superior = $user_dir->get_user_by_uid($superior_uid); - } catch(UserNotFoundException $e) { - } - } - if(is_null($superior)) { - $this->superior_entity_id = null; - } else { - $this->superior_entity_id = $superior->entity_id; - } - $this->update(); - } else { - throw new UserNotFoundException('User does not exist.'); - } - } - /** * Implements the Entity::sync_access as a no-op as it makes no sense to grant access TO a user. */ diff --git a/model/userdirectory.php b/model/userdirectory.php index b7db1ad..77b8a33 100644 --- a/model/userdirectory.php +++ b/model/userdirectory.php @@ -30,11 +30,8 @@ public function add_user(User $user) { $user_admin = $user->admin; $user_email = $user->email; try { - $stmt = $this->database->prepare("INSERT INTO entity SET type = 'user'"); - $stmt->execute(); - $user->entity_id = $stmt->insert_id; - $stmt = $this->database->prepare("INSERT INTO user SET entity_id = ?, uid = ?, name = ?, email = ?, active = ?, admin = ?, auth_realm = ?"); - $stmt->bind_param('dsssdds', $user->entity_id, $user_id, $user_name, $user_email, $user_active, $user_admin, $user->auth_realm); + $stmt = $this->database->prepare("INSERT INTO user SET uid = ?, name = ?, email = ?, active = ?, admin = ?, auth_realm = ?"); + $stmt->bind_param('sssdds', $user_id, $user_name, $user_email, $user_active, $user_admin, $user->auth_realm); $stmt->execute(); $stmt->close(); } catch(mysqli_sql_exception $e) { @@ -44,23 +41,23 @@ public function add_user(User $user) { } else { throw $e; } - } + } $user->log(array('action' => 'User add')); } /** - * Get a user from the database by its entity ID. - * @param int $entity_id of user - * @return User with specified entity ID - * @throws UserNotFoundException if no user with that entity ID exists + * Get a user from the database by its ID. + * @param int $id of user + * @return User with specified ID + * @throws UserNotFoundException if no user with that ID exists */ public function get_user_by_id($id) { - $stmt = $this->database->prepare("SELECT * FROM user WHERE entity_id = ?"); + $stmt = $this->database->prepare("SELECT * FROM user WHERE id = ?"); $stmt->bind_param('d', $id); $stmt->execute(); $result = $stmt->get_result(); if($row = $result->fetch_assoc()) { - $user = new User($row['entity_id'], $row); + $user = new User($row['id'], $row); } else { throw new UserNotFoundException('User does not exist.'); } @@ -72,42 +69,24 @@ public function get_user_by_id($id) { * Get a user from the database by its uid. If it does not exist in the database, retrieve it * from LDAP and store in the database. * @param string $uid of user - * @return User with specified entity uid + * @return User with specified uid * @throws UserNotFoundException if no user with that uid exists */ public function get_user_by_uid($uid) { global $config, $group_dir, $active_user; $ldap_enabled = $config['ldap']['enabled']; - $group_sync_enabled = $config['ldap']['full_group_sync']; try { $user = $this->_get_user_by_uid($uid); } catch(UserNotFoundException $e) { if ($ldap_enabled == 1) { - $active_user = $this->_get_user_by_uid('keys-sync'); + $active_user = $this->_get_user_by_uid('cert-sync'); $user = new User; $user->uid = $uid; $this->cache_uid[$uid] = $user; $user->auth_realm = 'LDAP'; $user->get_details_from_ldap(); - $ldap_groups = array_map(function($group) { - return $group["cn"]; - }, $user->ldapgroups); $this->add_user($user); - - if($group_sync_enabled == 1) { - foreach($ldap_groups as $group) { - try { - $grp = $group_dir->get_group_by_name($group); - } catch(GroupNotFoundException $e) { - $grp = new Group; - $grp->name = $group; - $grp->system = 1; - $group_dir->add_group($grp); - } - $grp->add_member($user); - } - } } else { throw new UserNotFoundException('User does not exist.'); } @@ -124,7 +103,7 @@ private function _get_user_by_uid($uid) { $stmt->execute(); $result = $stmt->get_result(); if($row = $result->fetch_assoc()) { - $user = new User($row['entity_id'], $row); + $user = new User($row['id'], $row); $this->cache_uid[$uid] = $user; } else { throw new UserNotFoundException('User does not exist.'); @@ -164,14 +143,14 @@ public function list_users($include = array(), $filter = array()) { SELECT ".implode(", ", $fields)." FROM user ".implode(" ", $joins)." ".(count($where) == 0 ? "" : "WHERE (".implode(") AND (", $where).")")." - GROUP BY user.entity_id + GROUP BY user.id ORDER BY user.uid "); $stmt->execute(); $result = $stmt->get_result(); $users = array(); while($row = $result->fetch_assoc()) { - $users[] = new User($row['entity_id'], $row); + $users[] = new User($row['id'], $row); } return $users; } diff --git a/model/userevent.php b/model/userevent.php index 3823150..30d9458 100644 --- a/model/userevent.php +++ b/model/userevent.php @@ -2,17 +2,27 @@ /** * Class that represents a log event that was recorded in relation to a group */ -class UserEvent extends EntityEvent { +class UserEvent extends Record { /** - * Magic getter method - if group field requested, return Group object of the affected group. + * Defines the database table that this object is stored in + */ + protected $table = 'user_event'; + + /** + * Magic getter method - if actor field requested, return User object of the person who triggered + * the logged event. * @param string $field to retrieve * @return mixed data stored in field */ public function &__get($field) { + global $user_dir; switch($field) { case 'user': - $user = new User($this->data['entity_id']); + $user = new User($this->data['user_id']); return $user; + case 'actor': + $actor = new User($this->data['actor_id']); + return $actor; default: return parent::__get($field); } diff --git a/pagesection.php b/pagesection.php index 7c0b6a7..bbd77a0 100644 --- a/pagesection.php +++ b/pagesection.php @@ -14,12 +14,10 @@ public function __construct($template) { $this->data->menu_items['/'] = 'Home'; $this->data->menu_items['/servers'] = 'Servers'; $this->data->menu_items['/users'] = 'Users'; - $this->data->menu_items['/groups'] = 'Groups'; - $this->data->menu_items['/pubkeys'] = 'Public keys'; - if($active_user && ($active_user->admin || count($active_user->list_admined_servers()) > 0)) { + if($active_user) { $this->data->menu_items['/activity'] = 'Activity'; } - if($active_user && $active_user->admin) { + if($active_user) { $this->data->menu_items['/tools'] = 'Tools'; } $this->data->menu_items['/help'] = 'Help'; diff --git a/public_html/style.css b/public_html/style.css index d849633..19454dd 100644 --- a/public_html/style.css +++ b/public_html/style.css @@ -37,7 +37,7 @@ body { .panel-group + p { margin-top: 1em; } -a.group, a.server, a.serveraccount, a.user { +a.group, a.server, a.user { white-space: nowrap; } a.group::before { @@ -63,17 +63,6 @@ a.server::before { top: 1px; padding-right: 0.4em; } -a.serveraccount::before { - content: "\e161"; - display: inline-block; - font-family: "Glyphicons Halflings"; - font-style: normal; - font-weight: 400; - line-height: 1; - position: relative; - top: 1px; - padding-right: 0.4em; -} a.user::before { content: "\e008"; display: inline-block; diff --git a/routes.php b/routes.php index ef1991e..a88763b 100644 --- a/routes.php +++ b/routes.php @@ -4,39 +4,16 @@ '/activity' => 'activity', '/bulk_mail' => 'bulk_mail', '/bulk_mail/{recipients}' => 'bulk_mail', - '/groups' => 'groups', - '/groups/{group}' => 'group', - '/groups/{group}/members.{format}' => 'group', - '/groups/{group}/access_rules/{access}' => 'access_options', '/help' => 'help', - '/pubkeys' => 'pubkeys', - '/pubkeys.{format}' => 'pubkeys', - '/pubkeys/{key}' => 'pubkey', - '/pubkeys/{key}.{format}' => 'pubkey', '/servers' => 'servers', '/servers.{format}' => 'servers', '/servers/{hostname}' => 'server', - '/servers/{hostname}/accounts/{account}' => 'serveraccount', - '/servers/{hostname}/accounts/{account}/access_rules/{access}' => 'access_options', - '/servers/{hostname}/accounts/{account}/pubkeys.{format}' => 'serveraccount_pubkeys', - '/servers/{hostname}/accounts/{account}/sync_status' => 'serveraccount_sync_status', '/servers/{hostname}/status.{format}' => 'server', - '/servers/{hostname}/sync_status' => 'server_sync_status', '/tools' => 'tools', '/users' => 'users', '/users/{username}' => 'user', - '/users/{username}/pubkeys' => 'user_pubkeys', - '/users/{username}/pubkeys.{format}' => 'user_pubkeys', - '/users/{username}/pubkeys/{key}' => 'pubkey', - '/users/{username}/pubkeys/{key}.{format}' => 'pubkey', ); $public_routes = array( - '/groups/{group}/members.{format}' => true, - '/pubkeys.{format}' => true, - '/pubkeys/{key}.{format}' => true, - '/servers/{hostname}/accounts/{account}/pubkeys.{format}' => true, '/users/{username}' => true, - '/users/{username}/pubkeys.{format}' => true, - '/users/{username}/pubkeys/{key}.{format}' => true, ); diff --git a/scripts/restrict.sh b/scripts/restrict.sh index e6ba5a7..b5f435f 100644 --- a/scripts/restrict.sh +++ b/scripts/restrict.sh @@ -4,12 +4,12 @@ cmd="$1"; shift; case "$cmd" in /usr/bin/env) case "${@}" in - "test -d /var/local/keys-sync") - /usr/bin/env test -d /var/local/keys-sync; + "test -d /var/local/cert-sync") + /usr/bin/env test -d /var/local/cert-sync; exit $?; ;; - "cat /var/local/keys-sync/.hostnames") - /usr/bin/env cat /var/local/keys-sync/.hostnames; + "cat /var/local/cert-sync/.hostnames") + /usr/bin/env cat /var/local/cert-sync/.hostnames; exit $?; ;; "hostname -f") @@ -20,23 +20,23 @@ case "$cmd" in /usr/bin/env hostname -f; exit $?; ;; - "sha1sum '/var/local/keys-sync'/*") - /usr/bin/env sha1sum '/var/local/keys-sync'/*; + "sha1sum '/var/local/cert-sync'/*") + /usr/bin/env sha1sum '/var/local/cert-sync'/*; exit $?; ;; "id "*) eval "${@}"; exit $?; ;; - "chmod 600 '/var/local/keys-sync/"*) + "chmod 600 '/var/local/cert-sync/"*) eval "${@}"; exit $?; ;; - "chown keys-sync: '/var/local/keys-sync/"*) + "chown cert-sync: '/var/local/cert-sync/"*) eval "${@}"; exit $?; ;; - "rm -f '/var/local/keys-sync/"*) + "rm -f '/var/local/cert-sync/"*) eval "${@}"; exit $?; ;; @@ -51,7 +51,7 @@ case "$cmd" in case "${1}" in "-t") case "${2}" in - "'/var/local/keys-sync/"*) + "'/var/local/cert-sync/"*) scp -t "$(echo ${2} | xargs)" < /dev/stdin; exit $?; ;; @@ -63,5 +63,5 @@ case "$cmd" in ;; *);; esac -echo "${cmd} ${@}" >> /var/local/keys-sync/.exec.log; +echo "${cmd} ${@}" >> /var/local/cert-sync/.exec.log; exit 1; diff --git a/scripts/sync.php b/scripts/sync.php index 5a0e557..8f8ea46 100755 --- a/scripts/sync.php +++ b/scripts/sync.php @@ -8,7 +8,7 @@ require('Net/SSH2.php'); require('Net/SCP.php'); -$required_files = array('config/keys-sync', 'config/keys-sync.pub'); +$required_files = array('config/cert-sync', 'config/cert-sync.pub'); foreach($required_files as $file) { if(!file_exists($file)) die("Sync cannot start - $file not found.\n"); } @@ -50,12 +50,12 @@ } $preview = isset($options['preview']); -// Use 'keys-sync' user as the active user (create if it does not yet exist) +// Use 'cert-sync' user as the active user (create if it does not yet exist) try { - $active_user = $user_dir->get_user_by_uid('keys-sync'); + $active_user = $user_dir->get_user_by_uid('cert-sync'); } catch(UserNotFoundException $e) { $active_user = new User; - $active_user->uid = 'keys-sync'; + $active_user->uid = 'cert-sync'; $active_user->name = 'Synchronization script'; $active_user->email = ''; $active_user->auth_realm = 'local'; @@ -146,14 +146,14 @@ function sync_server($id, $only_username = null, $preview = false) { global $server_dir; global $user_dir; - $keydir = '/var/local/keys-sync'; + $keydir = '/var/local/cert-sync'; $header = "## Auto generated keys file for %s ## Do not edit this file! Modify at %s "; $header_no_link = "## Auto generated keys file for %s ## Do not edit this file! "; - $ska_key = file_get_contents('config/keys-sync.pub'); + $sca_key = file_get_contents('config/cert-sync.pub'); $server = $server_dir->get_server_by_id($id); $hostname = $server->hostname; @@ -214,9 +214,9 @@ function sync_server($id, $only_username = null, $preview = false) { } } } - if(array_key_exists('keys-sync', $keyfiles)) { - // keys-sync account should never be synced - unset($keyfiles['keys-sync']); + if(array_key_exists('cert-sync', $keyfiles)) { + // cert-sync account should never be synced + unset($keyfiles['cert-sync']); } if($preview) { foreach($keyfiles as $username => $keyfile) { @@ -237,7 +237,7 @@ function sync_server($id, $only_username = null, $preview = false) { echo date('c')." {$hostname}: Attempting to connect.\n"; $legacy = false; - $attempts = array('keys-sync', 'root'); + $attempts = array('cert-sync', 'root'); foreach($attempts as $attempt) { try { $ssh = new Net_SSH2($hostname, $server->port); @@ -288,7 +288,7 @@ function sync_server($id, $only_username = null, $preview = false) { } try { $key = new Crypt_RSA(); - $key->loadKey(file_get_contents('config/keys-sync')); + $key->loadKey(file_get_contents('config/cert-sync')); if ($ssh->login($attempt, $key)) { echo date('c')." {$hostname}: Logged in as $attempt.\n"; break; @@ -326,8 +326,8 @@ function sync_server($id, $only_username = null, $preview = false) { // Verify that we have mutual agreement with the server that we sync to it with this hostname $allowed_hostnames = null; if($config['security']['hostname_verification'] >= 2) { - // 2+ = Compare with /var/local/keys-sync/.hostnames - $allowed_hostnames = explode("\n", trim($ssh->exec('/usr/bin/env cat /var/local/keys-sync/.hostnames'))); + // 2+ = Compare with /var/local/cert-sync/.hostnames + $allowed_hostnames = explode("\n", trim($ssh->exec('/usr/bin/env cat /var/local/cert-sync/.hostnames'))); echo implode("|",$allowed_hostnames); if(is_bool($ssh->getExitStatus()) || $ssh->getExitStatus() != 0) { if($config['security']['hostname_verification'] >= 3) { @@ -370,7 +370,7 @@ function sync_server($id, $only_username = null, $preview = false) { try { $local_filename = tempnam('/tmp', 'syncfile'); $fh = fopen($local_filename, 'w'); - fwrite($fh, $keyfile['keyfile']."# SKA system key\n".$ska_key); + fwrite($fh, $keyfile['keyfile']."# sca system key\n".$sca_key); fclose($fh); $ssh->exec('/usr/bin/env mkdir -p '.escapeshellarg('/root/.ssh')); if(!is_bool($ssh->getExitStatus()) && $ssh->getExitStatus() == 0) { @@ -453,7 +453,7 @@ function sync_server($id, $only_username = null, $preview = false) { } } if($success) { - $ssh->exec('/usr/bin/env chown keys-sync: '.escapeshellarg($remote_filename)); + $ssh->exec('/usr/bin/env chown cert-sync: '.escapeshellarg($remote_filename)); $success = !is_bool($ssh->getExitStatus()) && $ssh->getExitStatus() == 0; if(!$success) { echo "Unable to change ownership"; @@ -495,7 +495,7 @@ function sync_server($id, $only_username = null, $preview = false) { if(is_null($only_username)) { // Clean up directory foreach($sha1sums as $file => $sha1sum) { - if($file != '' && $file != 'keys-sync' && $file != '.hostnames') { + if($file != '' && $file != 'cert-sync' && $file != '.hostnames') { try { $remote_filename = "$keydir/$file"; $success = false; @@ -558,19 +558,6 @@ function get_keys($access_rules, $account_name, $hostname) { $keyfile .= "# Inactive account\n"; } break; - case 'ServerAccount': - $keyfile .= "# {$entity->name}@{$entity->server->hostname}"; - $keyfile .= " granted access by {$access->granted_by->uid} on {$grant_date_full}"; - $keyfile .= "\n"; - if($entity->server->key_management != 'decommissioned') { - $keys = $entity->list_public_keys($account_name, $hostname); - foreach($keys as $key) { - $keyfile .= $prefix.$key->export()."\n"; - } - } else { - $keyfile .= "# Decommissioned server\n"; - } - break; case 'Group': // Recurse! $seen = array($entity->name => true); @@ -606,18 +593,6 @@ function get_group_keys($entities, $account_name, $hostname, $prefix, &$seen) { $keyfile .= "# Inactive account\n"; } break; - case 'ServerAccount': - $keyfile .= "# {$entity->name}@{$entity->server->hostname}"; - $keyfile .= "\n"; - if($entity->server->key_management != 'decommissioned') { - $keys = $entity->list_public_keys($account_name, $hostname); - foreach($keys as $key) { - $keyfile .= $prefix.$key->export()."\n"; - } - } else { - $keyfile .= "# Decommissioned server\n"; - } - break; case 'Group': // Recurse! if(!isset($seen[$entity->name])) { diff --git a/scripts/syncd.php b/scripts/syncd.php index 57d4725..c12d5da 100755 --- a/scripts/syncd.php +++ b/scripts/syncd.php @@ -24,13 +24,13 @@ function dlog($txt) { chdir(__DIR__); error_reporting(E_ALL); ini_set('display_errors', 1); -cli_set_process_title('keys-sync'); +cli_set_process_title('cert-sync'); umask(027); if(!isset($options['systemd'])) { - $pidfile = '/var/run/keys-sync.pid'; - $lockfile = '/var/run/keys-sync.lock'; + $pidfile = '/var/run/cert-sync.pid'; + $lockfile = '/var/run/cert-sync.lock'; $logfile = '/var/log/keys/sync.log'; if(!isset($options['user'])) { diff --git a/services/README b/services/README index a5f5f6f..1b6d950 100644 --- a/services/README +++ b/services/README @@ -2,12 +2,12 @@ To install the sync service =========================== On a systemd system: - 1) Copy the systemd/keys-sync.service file to /etc/systemd/system/ - 2) Modify ExecStart path and User as necessary. If SSH Key Authority is installed under /home, disable ProtectHome. + 1) Copy the systemd/cert-sync.service file to /etc/systemd/system/ + 2) Modify ExecStart path and User as necessary. If SSL Cert Authority is installed under /home, disable ProtectHome. 3) Run: systemctl daemon-reload - 4) Run: systemctl enable keys-sync.service + 4) Run: systemctl enable cert-sync.service On a sysvinit system: - 1) Copy the init.d/keys-sync file to /etc/init.d/ + 1) Copy the init.d/cert-sync file to /etc/init.d/ 2) Modify SCRIPT path and USER as necessary. - 3) Run: update-rc.d keys-sync defaults + 3) Run: update-rc.d cert-sync defaults diff --git a/services/init.d/keys-sync b/services/init.d/keys-sync index 647a24f..76eb472 100644 --- a/services/init.d/keys-sync +++ b/services/init.d/keys-sync @@ -1,7 +1,7 @@ #!/usr/bin/env sh ### BEGIN INIT INFO -# Provides: keys-sync +# Provides: cert-sync # Required-Start: mysql # Required-Stop: mysql # Default-Start: 2 3 4 5 @@ -12,19 +12,19 @@ . /lib/lsb/init-functions SCRIPT=/srv/keys/scripts/syncd.php -USER=keys-sync -PIDFILE=/var/run/keys-sync.pid +USER=cert-sync +PIDFILE=/var/run/cert-sync.pid test -f $SCRIPT || exit 0 case "$1" in start) - log_daemon_msg "Starting keys-sync daemon" + log_daemon_msg "Starting cert-sync daemon" start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $SCRIPT --user $USER -- log_end_msg $? ;; stop) - log_daemon_msg "Stopping keys-sync daemon" + log_daemon_msg "Stopping cert-sync daemon" start-stop-daemon --stop --quiet --pidfile $PIDFILE --name syncd.php --user $USER log_end_msg $? rm -f $PIDFILE @@ -33,7 +33,7 @@ restart) $0 stop && $0 start ;; *) - log_action_msg "Usage: /etc/init.d/keys-sync {start|stop|restart}" + log_action_msg "Usage: /etc/init.d/cert-sync {start|stop|restart}" exit 2 ;; esac diff --git a/services/systemd/keys-sync.service b/services/systemd/keys-sync.service index 3158694..09415f3 100644 --- a/services/systemd/keys-sync.service +++ b/services/systemd/keys-sync.service @@ -6,7 +6,7 @@ Requires=mysql.service [Service] Type=simple ExecStart=/srv/keys/scripts/syncd.php --systemd -User=keys-sync +User=cert-sync StandardOutput=journal StandardError=journal PrivateDevices=on diff --git a/templates/access_options.php b/templates/access_options.php deleted file mode 100644 index b38fc33..0000000 --- a/templates/access_options.php +++ /dev/null @@ -1,124 +0,0 @@ -get('entity'); -switch(get_class($entity)) { - case 'ServerAccount': $account = $entity; $server = $entity->server; break; - case 'Group': $group = $entity; break; -} -$remote_entity = $this->get('remote_entity'); -$mode = $this->get('mode'); -$options = $this->get('options'); -switch(get_class($remote_entity)) { - case 'User': $remote_entity_name = $remote_entity->uid; break; - case 'ServerAccount': $remote_entity_name = $remote_entity->name.'@'.$remote_entity->server->hostname; break; - case 'Group': $remote_entity_name = $remote_entity->name; break; -} -?> -

access

-
- get('active_user')->get_csrf_field(), ESC_NONE) ?> - uid); - ?> - - server->hostname).'/accounts/'.urlencode($remote_entity->name); - ?> - - - name); - ?> - - -

- You are SSH access to - - name.'@'.$server->hostname)?> - - resources in the name)?> group - - for - . -

- -
-
-
- -
- -
-

- Presets: - - - -

-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- - Cancel - - Cancel - -
-
-
diff --git a/templates/base.php b/templates/base.php index 1f54849..262fa10 100644 --- a/templates/base.php +++ b/templates/base.php @@ -25,7 +25,7 @@ - SSH Key Authority + SSL Cert Authority @@ -50,7 +50,7 @@ @@ -192,7 +192,7 @@

While this sync method is simpler to set up, we recommend setting up full account syncing where possible.

Add the following to the /root/.ssh/authorized_keys file (create it if it does not exist):

-
get('keys-sync-pubkey'))?>
+
get('cert-sync-pubkey'))?>

The /root and /root/.ssh directories must be accessible only by root.

@@ -214,19 +214,19 @@
  • Click "Manage account" for the relevant account.
  • In the "Add user to account" form, enter the user's intranet account name and submit.
  • -

    For server-to-server access, assuming that both of the servers involved are managed by SSH Key Authority:

    +

    For server-to-server access, assuming that both of the servers involved are managed by SSL Cert Authority:

    Example: foo@source.example.com needs SSH access to bar@destination.example.com

    1. Go to the admin page for source.example.com (ie. /servers/source.example.com).
    2. -
    3. Add the "foo" account to keys ("Manage this account with SSH Key Authority") if it is not already listed.
    4. +
    5. Add the "foo" account to keys ("Manage this account with SSL Cert Authority") if it is not already listed.
    6. Go to the manage account page for "foo".
    7. On the Public keys tab, add the SSH public key for the foo@source.example.com account.
    8. Go to the admin page for destination.example.com (ie. /servers/destination.example.com).
    9. -
    10. Add the "bar" account to keys ("Manage this account with SSH Key Authority") if it is not already listed.
    11. +
    12. Add the "bar" account to keys ("Manage this account with SSL Cert Authority") if it is not already listed.
    13. Go to the manage account page for "bar".
    14. On the Access tab, add server-to-server access for foo@source.example.com.
    -

    In the above example if source.example.com is not yet known by SSH Key Authority, please contact to add it to the system.

    +

    In the above example if source.example.com is not yet known by SSL Cert Authority, please contact to add it to the system.

    diff --git a/templates/home.php b/templates/home.php index 0f93233..b91d75b 100644 --- a/templates/home.php +++ b/templates/home.php @@ -1,5 +1,5 @@

    Keys management

    -

    Welcome to the SSH Key Authority server.

    +

    Welcome to the SSL Cert Authority server.

    get('user_keys')) == 0) { ?>

    Getting started

    To start using the key management system, you must first generate a "key pair". The instructions for doing this vary based on your computer's Operating System (OS).

    @@ -156,7 +156,7 @@
    list_accounts() as $server_account) { ?> -
    name) ?>:
    +
    name) ?>:
    list_access() as $access) { @@ -165,9 +165,6 @@ case 'User': $list[] = hesc($entity->uid); break; - case 'ServerAccount': - $list[] = hesc($entity->name.'@'.$entity->server->hostname); - break; case 'Group': $list[] = ' '.hesc($entity->name); break; diff --git a/templates/not_admin.php b/templates/not_admin.php deleted file mode 100644 index 0398dfc..0000000 --- a/templates/not_admin.php +++ /dev/null @@ -1,2 +0,0 @@ -

    Unable to fulfill request

    -

    Your request cannot be fulfilled because you are not an administrator of the target entity.

    diff --git a/templates/pubkey.php b/templates/pubkey.php deleted file mode 100644 index 88a321e..0000000 --- a/templates/pubkey.php +++ /dev/null @@ -1,149 +0,0 @@ -get('pubkey')->owner; -?> -

    - Public key 'get('pubkey')->comment)?>' for - name; - ?> - - name.'@'.$owner->server->hostname; - ?> - - -

    -get('user_is_owner') || $this->get('admin')) { ?> - - -
    -
    -

    Information

    -
    -
    Key data
    -
    get('pubkey')->export())?>
    -
    Key size
    -
    get('pubkey')->keysize)?>
    -
    Fingerprint (MD5)
    -
    get('pubkey')->fingerprint_md5)?>
    -
    Randomart (MD5)
    -
    get('pubkey')->randomart_md5)?>
    -
    Fingerprint (SHA256)
    -
    get('pubkey')->fingerprint_sha256)?>
    -
    Randomart (SHA256)
    -
    get('pubkey')->randomart_sha256)?>
    -
    Upload Date
    -
    get('pubkey')->upload_date) ?>
    - config()['general']['key_expiration_enabled'] == 1) { ?> -
    Expiration Date
    -
    - get('pubkey')->upload_date; - $expiration_days = $this->config()['general']['key_expiration_days']; - $expiration_date = strtotime($date . ' + ' . $expiration_days . ' days'); - $expiration_time_in_days = round(($expiration_date - time()) / (60 * 60 * 24)); - out(date('Y-m-d H:i:s', $expiration_date) . ' (' . $expiration_time_in_days . ' days left)'); - ?> -
    - -
    -
    - get('user_is_owner') || $this->get('admin')) { ?> -
    -

    Key signing

    -
    - get('signatures')) == 0) { ?> -

    No signatures have been uploaded for this key yet.

    - - - - - - - - - - - - get('signatures') as $sig) { ?> - - - - - - - - -
    Signing keySigned onUploaded onActions
    fingerprint)?>sign_date)?>upload_date)?>
    - -

    Add signature

    - get('active_user')->get_csrf_field(), ESC_NONE) ?> -
    - -
    -
    - -
    -
    -
    -
    -

    Destination restrictions

    - get('dest_rules')) == 0) { ?> -

    This key will currently be synced to all accounts and servers that is granted access to. To restrict this key to a subset of that list, add rules below.

    - -

    This key will only be synced to accounts and servers that is granted access to that also match the following rules:

    -
    - get('active_user')->get_csrf_field(), ESC_NONE) ?> - - - - - - - - - - get('dest_rules') as $rule) { ?> - - - - - - - -
    Account nameHostnameActions
    account_name_filter)?>hostname_filter)?>
    -
    - -
    - get('active_user')->get_csrf_field(), ESC_NONE) ?> -

    Add new rule

    -

    You can make use of wildcards (*) in each field below.

    -
    - - -
    -
    - - -
    -
    - -
    -
    -
    - -
    diff --git a/templates/pubkey_json.php b/templates/pubkey_json.php deleted file mode 100644 index c43b73b..0000000 --- a/templates/pubkey_json.php +++ /dev/null @@ -1,4 +0,0 @@ -get('pubkey'); -$json = pubkey_json($pubkey); -out(json_encode($json), ESC_NONE); diff --git a/templates/pubkey_txt.php b/templates/pubkey_txt.php deleted file mode 100644 index 45a49ea..0000000 --- a/templates/pubkey_txt.php +++ /dev/null @@ -1,3 +0,0 @@ -get('pubkey'); -out($pubkey->export()."\n", ESC_NONE); diff --git a/templates/pubkeys.php b/templates/pubkeys.php deleted file mode 100644 index 36fed47..0000000 --- a/templates/pubkeys.php +++ /dev/null @@ -1,88 +0,0 @@ -

    Public keys

    -
    -
    -
    -

    Filter options

    -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    -
    - -
    - - -
    -
    -
    - -
    -
    -
    -
    -
    -

    get('pubkeys')); out(number_format($total).' public key'.($total == 1 ? '' : 's').' found')?>

    - - - - - - - - - - - - get('pubkeys') as $pubkey) { - ?> - - - - keysize < 4095) out(' class="danger"', ESC_NONE)?>>keysize)?> - - - - - -
    FingerprintTypeSizeCommentOwner
    - - fingerprint_md5)?> - fingerprint_sha256)?> - - type)?>comment)?> - owner)) { - case 'User': - ?> - owner->uid)?> - owner->active) out(' Inactive', ESC_NONE) ?> - - owner->name.'@'.$pubkey->owner->server->hostname)?> - owner->server->key_management == 'decommissioned') out(' Inactive', ESC_NONE) ?> - -
    diff --git a/templates/pubkeys_json.php b/templates/pubkeys_json.php deleted file mode 100644 index d8915ec..0000000 --- a/templates/pubkeys_json.php +++ /dev/null @@ -1,7 +0,0 @@ -public_keys = array(); -foreach($this->get('pubkeys') as $pubkey) { - $json->public_keys[] = pubkey_json($pubkey, false, true); -} -out(json_encode($json), ESC_NONE); diff --git a/templates/server.php b/templates/server.php index 1f4c7a1..e57a0cc 100644 --- a/templates/server.php +++ b/templates/server.php @@ -101,7 +101,7 @@ ?> - name) ?> + name) ?> pending_requests > 0) { ?> pending_requests))?> @@ -136,12 +136,6 @@ ?> uid) ?> name); if(!$entity->active) out(' Inactive', ESC_NONE)?> - - name.'@'.$entity->server->hostname) ?> - Server-to-server accessserver->key_management == 'decommissioned') out(' Inactive', ESC_NONE) ?> Account name - +
    @@ -266,7 +260,7 @@
    @@ -356,7 +350,7 @@
    get('server')->key_management) { - case 'keys': out('SSH keys managed and synced by SSH Key Authority'); break; + case 'keys': out('SSH keys managed and synced by SSL Cert Authority'); break; case 'none': out('Disabled - server has no key management'); break; case 'other': out('Disabled - SSH keys managed by another system'); break; case 'decommissioned': out('Disabled - server has been decommissioned'); break; @@ -641,7 +635,7 @@ ?> - name) ?> + name) ?> pending_requests > 0) { ?> pending_requests))?> @@ -674,12 +668,6 @@ ?> uid) ?> name); if(!$entity->active) out(' Inactive', ESC_NONE)?> - - name.'@'.$entity->server->hostname) ?> - Server-to-server accessserver->key_management == 'decommissioned') out(' Inactive', ESC_NONE) ?> get('account')->sync_status) { -case 'proposed': $sync_class = 'info'; $sync_message = 'Requested'; break; -case 'sync success': $sync_class = 'success'; $sync_message = 'Synced'; break; -case 'sync failure': $sync_class = 'danger'; $sync_message = 'Failed'; break; -case 'sync warning': -default: $sync_class = 'warning'; $sync_message = 'Not synced'; break; -} -?> -

    get('account')->name)?>@get('server')->hostname)?>get('account')->active == 0) out(' Inactive', ESC_NONE) ?>

    -get('server')->key_management == 'keys') { ?> -get('account')->name != 'root' && $this->get('server')->sync_status == 'sync warning') { ?> -
    - Non-root accounts are not being synchronized on this server yet. See the help pages for details of what is required to activate syncing for all accounts.

    -
    - -
    -
    Sync status:
    -
    get('account')->sync_is_pending()) { ?> - data-class="" data-message="" - - > - -
    -
    -
    - - -get('account')->sync_status == 'proposed') { ?> -
    - The account name get('account')->name) ?> is a requested account. If you reject the access requestget('access_requests')) == 1 ? '' : 's')?> below then the account will be removed from the keys system. -
    - - - - -
    - get('server')->key_management == 'keys') { ?> -
    -

    Access

    - get('access')) == 0 && count($this->get('access_requests')) == 0) { ?> -

    No-one has been granted access to this account.

    - -
    - get('active_user')->get_csrf_field(), ESC_NONE) ?> - - - - - - - - - - - get('access') as $access) { ?> - source_entity; ?> - - list_options(); - switch(get_class($entity)) { - case 'User': - ?> - - - - - - - - - - - - get('access_requests') as $access) { ?> - source_entity; ?> - - - - - - - - - - - - - - -
    Access forStatusOptionsActions
    - uid) ?> - active) out(' Inactive', ESC_NONE) ?> - - name.'@'.$entity->server->hostname) ?> - server->key_management == 'decommissioned') out(' Inactive', ESC_NONE) ?> - - name) ?> - active) out(' Inactive', ESC_NONE) ?> - - Access granted on grant_date) ?> by granted_by->uid) ?> - - 0) { ?> -
      - -
    • - - option); - if(!is_null($option->value)) { - ?>="" - -
    • - -
    - -
    - Configure access - -
    - uid) ?> - active) out(' Inactive', ESC_NONE) ?> - - name.'@'.$entity->server->hostname) ?> - server->key_management == 'decommissioned') out(' Inactive', ESC_NONE) ?> - name) ?>Access requested on request_date) ?> by requested_by->uid) ?> - - -
    -
    - -
    - get('active_user')->get_csrf_field(), ESC_NONE) ?> -

    Add user to account

    -
    -
    -
    - - - - get('all_users') as $user) { ?> - -
    -
    -
    - -
    -
    -
    -
    - get('active_user')->get_csrf_field(), ESC_NONE) ?> -

    Add server-to-server access to account

    -
    -
    -
    - Account name - -
    -
    -
    -
    - - - - get('all_servers') as $server) { ?> - -
    -
    -
    - -
    -
    -
    -
    - get('active_user')->get_csrf_field(), ESC_NONE) ?> -

    Add group access to account

    -
    -
    -
    - - - - get('all_groups') as $group) { ?> - -
    -
    -
    - -
    -
    -
    - get('group_membership')) > 0) { ?> -
    -

    Group access rules

    -

    - As this account is a member of the - get('group_membership') as $group) { - $grouplist[] = ''.hesc($group->name).''; - } - $grouplisttext = english_list($grouplist).' group'.(count($this->get('group_membership')) == 1 ? '' : 's'); - ?> - , the following access rules automatically apply to it: -

    - get('group_membership') as $group) { - $group_access_rules = $group->list_access(); - if(count($group_access_rules) > 0) { - ?> -

    name)?>

    - - - - - - - - - - - source_entity; ?> - - list_options(); - switch(get_class($entity)) { - case 'User': - ?> - - - - - - - - - - - - - - -
    Access forStatusOptions
    uid) ?>name); if(!$entity->active) out(' Inactive', ESC_NONE) ?>name.'@'.$entity->server->hostname) ?>Server-to-server accessserver->key_management == 'decommissioned') out(' Inactive', ESC_NONE) ?>name) ?>Group accessAccess granted on grant_date) ?> by granted_by->uid) ?> - 0) { ?> -
      - -
    • - - option); - if(!is_null($option->value)) { - ?>="" - -
    • - -
    - -
    - - - -
    - -
    -

    Public keys

    -

    Keys added here will be used for outgoing connections from this account to any account that it has been granted remote access to.

    -

    Public keys can be added to an account to facilitate server-to-server access from it to other accounts.

    - get('pubkeys')) == 0) { ?> -

    This account does not have any public keys associated with it.

    - -
    - get('active_user')->get_csrf_field(), ESC_NONE) ?> - - - - - - - - - - - - - get('pubkeys') as $key) { ?> - - - - - - - - - - -
    TypeFingerprintSizeCommentActions
    type) ?> - - fingerprint_md5) ?> - fingerprint_sha256) ?> - - - list_signatures()) > 0) { ?> - list_destination_rules()) > 0) { ?> - keysize) ?>comment) ?>
    -
    - -
    - get('active_user')->get_csrf_field(), ESC_NONE) ?> -
    - - -
    -
    -
    -
    -
    -

    Outbound access

    - get('remote_access')) == 0) { ?> -

    This account has not been granted access to any other resources.

    - -

    This account has access to the following resources:

    - - - - - - - - - get('remote_access') as $access) { ?> - dest_entity; ?> - - - - - - - - - - - - - - - -
    Access toStatus
    uid) ?>name); if(!$entity->active) out(' Inactive', ESC_NONE) ?>name.'@'.$entity->server->hostname) ?>Server-to-server accessserver->key_management == 'decommissioned') out(' Inactive', ESC_NONE) ?>name) ?>Group accessAccess granted on grant_date) ?> by granted_by->uid) ?>
    - - get('group_membership')) > 0) { ?> -
    -

    Group access rules

    -

    - As this account is a member of the - get('group_membership') as $group) { - $grouplist[] = ''.hesc($group->name).''; - } - $grouplisttext = english_list($grouplist).' group'.(count($this->get('group_membership')) == 1 ? '' : 's'); - ?> - , the following outbound access rules automatically apply to it: -

    - get('group_membership') as $group) { - $group_access_rules = $group->list_remote_access(); - if(count($group_access_rules) > 0) { - ?> -

    name)?>

    - - - - - - - - - - dest_entity; ?> - - - - - - - - - - - - - - - -
    Group has access toStatus
    uid) ?>name); if(!$entity->active) out(' Inactive', ESC_NONE) ?>name.'@'.$entity->server->hostname) ?>Server-to-server accessserver->key_management == 'decommissioned') out(' Inactive', ESC_NONE) ?>name) ?>Group accessAccess granted on grant_date) ?> by granted_by->uid) ?>
    - - - -
    -
    -

    Account administrators

    - get('admins')) == 0) { ?> -

    This account does not have any administrators assigned.

    - -
    - get('active_user')->get_csrf_field(), ESC_NONE) ?> - - - - - - get('admin') || $this->get('server_admin')) { ?> - - - - - - get('admins') as $admin) { ?> - - - - get('admin') || $this->get('server_admin')) { ?> - - - - - -
    User IDNameActions
    uid) ?>name); if(!$admin->active) out(' Inactive', ESC_NONE) ?> - -
    -
    - - get('admin') || $this->get('server_admin')) { ?> -
    - get('active_user')->get_csrf_field(), ESC_NONE) ?> -

    Add administrator

    -
    - - - - get('all_users') as $user) { ?> - -
    - -
    - -
    -
    -

    Log

    - - - - - - - - - - - - - - - get('log') as $event) { - show_event($event); - } - ?> - -
    EntityUserActivityDate (UTC)
    -
    -
    diff --git a/templates/serveraccount_sync_status_json.php b/templates/serveraccount_sync_status_json.php deleted file mode 100644 index 69c3cce..0000000 --- a/templates/serveraccount_sync_status_json.php +++ /dev/null @@ -1,7 +0,0 @@ -get('sync_status'); -$pending = $this->get('pending'); -$json = new StdClass; -$json->sync_status = $sync_status; -$json->pending = $pending; -out(json_encode($json), ESC_NONE); diff --git a/templates/servers.php b/templates/servers.php index 85af0c8..957d5eb 100644 --- a/templates/servers.php +++ b/templates/servers.php @@ -34,7 +34,7 @@

    Key management

    diff --git a/templates/user.php b/templates/user.php index 32f4af7..ba84ef7 100644 --- a/templates/user.php +++ b/templates/user.php @@ -114,11 +114,6 @@ dest_entity)) { - case 'ServerAccount': - ?> - dest_entity->name.'@'.$access->dest_entity->server->hostname)?> - dest_entity->name)?> diff --git a/templates/user_pubkeys.php b/templates/user_pubkeys.php deleted file mode 100644 index 8d7bae0..0000000 --- a/templates/user_pubkeys.php +++ /dev/null @@ -1,29 +0,0 @@ -

    Public keys for get('user')->name)?>

    -get('pubkeys') as $pubkey) { ?> -
    -
    -
    Key data
    -
    export())?>
    -
    Key size
    -
    keysize)?>
    -
    Fingerprint (MD5)
    -
    fingerprint_md5)?>
    -
    Fingerprint (SHA256)
    -
    fingerprint_sha256)?>
    -
    Upload Date
    -
    upload_date) ?>
    - config()['general']['key_expiration_enabled'] == 1) { ?> -
    Expiration Date
    -
    - upload_date; - $expiration_days = $this->config()['general']['key_expiration_days']; - $expiration_date = strtotime($date . ' + ' . $expiration_days . ' days'); - $expiration_time_in_days = round(($expiration_date - time()) / (60 * 60 * 24)); - out(date('Y-m-d H:i:s', $expiration_date) . ' (' . $expiration_time_in_days . ' days left)'); - ?> -
    - -
    -
    - diff --git a/views/access_options.php b/views/access_options.php deleted file mode 100644 index fad0739..0000000 --- a/views/access_options.php +++ /dev/null @@ -1,88 +0,0 @@ -vars['hostname'])) { - try { - $server = $server_dir->get_server_by_hostname($router->vars['hostname']); - $server_admin = $active_user->admin_of($server); - $account_admin = false; - if(!$server_admin && !$active_user->admin) { - try { - $account = $server->get_account_by_name($router->vars['account']); - $account_admin = $active_user->admin_of($account); - } catch(ServerAccountNotFoundException $e) { - } - if(!$account_admin) { - require('views/error403.php'); - die; - } - } else { - $account = $server->get_account_by_name($router->vars['account']); - } - $access = $account->get_access_by_id($router->vars['access']); - $entity = $account; - } catch(ServerNotFoundException $e) { - require('views/error404.php'); - die; - } catch(ServerAccountNotFoundException $e) { - require('views/error404.php'); - die; - } catch(AccessNotFoundException $e) { - require('views/error404.php'); - die; - } -} elseif(isset($router->vars['group'])) { - try { - $group = $group_dir->get_group_by_name($router->vars['group']); - $group_admin = $active_user->admin_of($group); - $access = $group->get_access_by_id($router->vars['access']); - $entity = $group; - } catch(GroupNotFoundException $e) { - require('views/error404.php'); - die; - } catch(AccessNotFoundException $e) { - require('views/error404.php'); - die; - } -} else { - require('views/error404.php'); - die; -} -if(isset($_POST['update_access'])) { - $options = array(); - if(isset($_POST['access_option'])) { - foreach($_POST['access_option'] as $k => $v) { - if($v['enabled']) { - $option = new AccessOption(); - $option->option = $k; - if(isset($v['value'])) { - $option->value = $v['value']; - } else { - $option->value = null; - } - $options[] = $option; - } - } - } - $access->update_options($options); - if(isset($server)) { - redirect('/servers/'.urlencode($router->vars['hostname']).'/accounts/'.urlencode($router->vars['account']).'#access'); - } elseif(isset($group)) { - redirect('/groups/'.urlencode($router->vars['group']).'#access'); - } -} else { - $content = new PageSection('access_options'); - $content->set('entity', $entity); - $content->set('options', $access->list_options()); - $content->set('admin', $active_user->admin); - $content->set('remote_entity', $access->source_entity); - $content->set('mode', 'edit'); -} - -$page = new PageSection('base'); -if(isset($server)) { - $page->set('title', $account->name.'@'.$server->hostname); -} elseif(isset($group)) { - $page->set('title', $group->name); -} -$page->set('content', $content); -$page->set('alerts', $active_user->pop_alerts()); -echo $page->generate(); diff --git a/views/group.php b/views/group.php deleted file mode 100644 index fdf87ba..0000000 --- a/views/group.php +++ /dev/null @@ -1,174 +0,0 @@ -get_group_by_name($router->vars['group']); -} catch(GroupNotFoundException $e) { - require('views/error404.php'); - die; -} -$all_users = $user_dir->list_users(); -$all_groups = $group_dir->list_groups(); -$all_servers = $server_dir->list_servers(); -$admined_servers = $active_user->list_admined_servers(); -$group_members = $group->list_members(); -$group_access = $group->list_access(); -$group_remote_access = $group->list_remote_access(); -$group_admins = $group->list_admins(); -$group_admin = $active_user->admin_of($group); - -if(isset($_POST['add_admin']) && ($active_user->admin)) { - try { - $user = $user_dir->get_user_by_uid($_POST['user_name']); - } catch(UserNotFoundException $e) { - $content = new PageSection('user_not_found'); - } - if(isset($user)) { - $group->add_admin($user); - redirect('#admins'); - } -} elseif(isset($_POST['delete_admin']) && ($active_user->admin)) { - foreach($group_admins as $admin) { - if($admin->id == $_POST['delete_admin']) { - $admin_to_delete = $admin; - } - } - if(isset($admin_to_delete)) { - $group->delete_admin($admin_to_delete); - } - redirect('#admins'); -} elseif(isset($_POST['add_member']) && ($group_admin || $active_user->admin)) { - if(isset($_POST['username'])) { - try { - $entity = $user_dir->get_user_by_uid(trim($_POST['username'])); - } catch(UserNotFoundException $e) { - $content = new PageSection('user_not_found'); - } - } elseif(isset($_POST['account'])) { - try { - $server = $server_dir->get_server_by_hostname(trim($_POST['hostname'])); - $entity = $server->get_account_by_name(trim($_POST['account'])); - } catch(ServerNotFoundException $e) { - $content = new PageSection('server_not_found'); - } catch(ServerAccountNotFoundException $e) { - $content = new PageSection('server_account_not_found'); - } - } - if(isset($entity) && !$group->system) { - try { - $group->add_member($entity); - redirect('#members'); - } catch(InvalidArgumentException $e) { - $content = new PageSection('not_admin'); - } - } -} elseif(isset($_POST['delete_member']) && ($group_admin || $active_user->admin)) { - foreach($group_members as $member) { - if($member->entity_id == $_POST['delete_member']) { - $member_to_delete = $member; - } - } - if(isset($member_to_delete) && !$group->system) { - $group->delete_member($member_to_delete); - } - redirect('#members'); -} elseif(isset($_POST['add_access']) && ($group_admin || $active_user->admin)) { - if(isset($_POST['username'])) { - try { - $entity = $user_dir->get_user_by_uid(trim($_POST['username'])); - } catch(UserNotFoundException $e) { - $content = new PageSection('user_not_found'); - } - } elseif(isset($_POST['account'])) { - try { - $server = $server_dir->get_server_by_hostname(trim($_POST['hostname'])); - $entity = $server->get_account_by_name(trim($_POST['account'])); - } catch(ServerNotFoundException $e) { - $content = new PageSection('server_not_found'); - } catch(ServerAccountNotFoundException $e) { - $content = new PageSection('server_account_not_found'); - } - } elseif(isset($_POST['group'])) { - try { - $entity = $group_dir->get_group_by_name(trim($_POST['group'])); - } catch(GroupNotFoundException $e) { - $content = new PageSection('group_not_found'); - } - } - if(isset($entity)) { - if($_POST['add_access'] == '2') { - $options = array(); - if(isset($_POST['access_option'])) { - foreach($_POST['access_option'] as $k => $v) { - if(isset($v['enabled'])) { - $option = new AccessOption(); - $option->option = $k; - if(isset($v['value'])) { - $option->value = $v['value']; - } else { - $option->value = null; - } - $options[] = $option; - } - } - } - $group->add_access($entity, $options); - redirect('#access'); - } else { - $content = new PageSection('access_options'); - $content->set('entity', $group); - $content->set('remote_entity', $entity); - $content->set('mode', 'create'); - } - } -} elseif(isset($_POST['delete_access']) && ($group_admin || $active_user->admin)) { - foreach($group_access as $access) { - if($access->id == $_POST['delete_access']) { - $access_to_delete = $access; - } - } - if(isset($access_to_delete)) { - $group->delete_access($access_to_delete); - } - redirect('#access'); -} elseif(isset($_POST['edit_group']) && ($active_user->admin)) { - $name = trim($_POST['name']); - $group->name = $name; - $group->active = $_POST['active']; - try { - $group->update(); - $alert = new UserAlert; - $alert->content = "Settings saved."; - $active_user->add_alert($alert); - redirect('/groups/'.urlencode($name).'#settings'); // Must specify, since the name may have changed - } catch(UniqueKeyViolationException $e) { - $content = new PageSection('unique_key_violation'); - $content->set('exception', $e); - } -} else { - if(isset($router->vars['format']) && $router->vars['format'] == 'json') { - $page = new PageSection('group_json'); - $page->set('group_members', $group_members); - header('Content-type: application/json; charset=utf-8'); - echo $page->generate(); - exit; - } else { - $content = new PageSection('group'); - $content->set('group', $group); - $content->set('admin', $active_user->admin); - $content->set('group_admin', $group_admin); - $content->set('group_admins', $group_admins); - $content->set('group_members', $group_members); - $content->set('group_access', $group_access); - $content->set('group_remote_access', $group_remote_access); - $content->set('group_log', $group->get_log()); - $content->set('all_users', $all_users); - $content->set('all_groups', $all_groups); - $content->set('all_servers', $all_servers); - $content->set('admined_servers', $admined_servers); - } -} - -$page = new PageSection('base'); -$page->set('title', $group->name); -$page->set('content', $content); -$page->set('alerts', $active_user->pop_alerts()); -echo $page->generate(); diff --git a/views/groups.php b/views/groups.php deleted file mode 100644 index ccb323c..0000000 --- a/views/groups.php +++ /dev/null @@ -1,58 +0,0 @@ -admin)) { - $name = trim($_POST['name']); - if(preg_match('|/|', $name)) { - $content = new PageSection('invalid_group_name'); - $content->set('group_name', $name); - } else { - try { - $new_admin = $user_dir->get_user_by_uid(trim($_POST['admin_uid'])); - } catch(UserNotFoundException $e) { - $content = new PageSection('user_not_found'); - } - if(isset($new_admin)) { - $group = new Group; - $group->name = $name; - try { - $group_dir->add_group($group); - $group->add_admin($new_admin); - $alert = new UserAlert; - $alert->content = 'Group \''.hesc($name).'\' successfully created.'; - $alert->escaping = ESC_NONE; - $active_user->add_alert($alert); - } catch(GroupAlreadyExistsException $e) { - $alert = new UserAlert; - $alert->content = 'Group \''.hesc($name).'\' already exists.'; - $alert->escaping = ESC_NONE; - $alert->class = 'danger'; - $active_user->add_alert($alert); - } - redirect('#add'); - } - } -} else { - $defaults = array(); - $defaults['active'] = array('1'); - $defaults['name'] = ''; - $filter = simplify_search($defaults, $_GET); - try { - $groups = $group_dir->list_groups(array('admins', 'members'), $filter); - } catch(GroupSearchInvalidRegexpException $e) { - $groups = array(); - $alert = new UserAlert; - $alert->content = "The group name search pattern '".$filter['hostname']."' is invalid."; - $alert->class = 'danger'; - $active_user->add_alert($alert); - } - $content = new PageSection('groups'); - $content->set('filter', $filter); - $content->set('admin', $active_user->admin); - $content->set('groups', $groups); - $content->set('all_users', $user_dir->list_users()); -} - -$page = new PageSection('base'); -$page->set('title', 'Groups'); -$page->set('content', $content); -$page->set('alerts', $active_user->pop_alerts()); -echo $page->generate(); diff --git a/views/help.php b/views/help.php index bd19378..7732b2a 100644 --- a/views/help.php +++ b/views/help.php @@ -1,9 +1,9 @@ set('keys-sync-pubkey', file_get_contents('config/keys-sync.pub')); +if(file_exists('config/cert-sync.pub')) { + $content->set('cert-sync-pubkey', file_get_contents('config/cert-sync.pub')); } else { - $content->set('keys-sync-pubkey', 'Error: keyfile missing'); + $content->set('cert-sync-pubkey', 'Error: keyfile missing'); } $content->set('admin_mail', $config['email']['admin_address']); $content->set('baseurl', $config['web']['baseurl']); diff --git a/views/pubkey.php b/views/pubkey.php deleted file mode 100644 index 2e8f96c..0000000 --- a/views/pubkey.php +++ /dev/null @@ -1,110 +0,0 @@ -vars['key'])) { - try { - $pubkey = $pubkey_dir->get_public_key_by_id($router->vars['key']); - } catch(PublicKeyNotFoundException $e) { - require('views/error404.php'); - die; - } -} else { - $pubkeys = $pubkey_dir->list_public_keys(array(), array('fingerprint' => $router->vars['key'])); - if(count($pubkeys) == 1) { - redirect('/pubkeys/'.urlencode($pubkeys[0]->id)); - } elseif(count($pubkeys) > 1) { - redirect('/pubkeys?fingerprint='.urlencode($router->vars['key'])); - } else { - require('views/error404.php'); - } - exit; -} -$dest_rules = $pubkey->list_destination_rules(); -$signatures = $pubkey->list_signatures(); -$user_is_owner = false; -switch(get_class($pubkey->owner)) { -case 'User': - $title = 'Public key '.$pubkey->comment.' for '.$pubkey->owner->name; - if($pubkey->owner->uid == $active_user->uid) { - $user_is_owner = true; - } - break; -case 'ServerAccount': - $title = 'Public key '.$pubkey->comment.' for '.$pubkey->owner->name.'@'.$pubkey->owner->server->hostname; - if($active_user->admin_of($pubkey->owner) || $active_user->admin_of($pubkey->owner->server)) { - $user_is_owner = true; - } - break; -default: - require('views/error404.php'); - die; -} -if(isset($router->vars['format']) && $router->vars['format'] == 'txt') { - $page = new PageSection('pubkey_txt'); - $page->set('pubkey', $pubkey); - header('Content-type: text/plain; charset=utf-8'); - echo $page->generate(); -} elseif(isset($router->vars['format']) && $router->vars['format'] == 'json') { - $page = new PageSection('pubkey_json'); - $page->set('pubkey', $pubkey); - header('Content-type: application/json; charset=utf-8'); - echo $page->generate(); -} else { - if(isset($_POST['add_signature']) && ($user_is_owner || $active_user->admin)) { - $sig = new PublicKeySignature; - $sig->signature = file_get_contents($_FILES['signature']['tmp_name']); - $sig->public_key = $pubkey; - try { - $pubkey->add_signature($sig); - redirect('#sig'); - } catch(InvalidArgumentException $e) { - $content = new PageSection('signature_upload_fail'); - switch($e->getMessage()) { - case "Signature doesn't validate against pubkey": - $content->set('message', "The signature you submitted doesn't seem to validate against this public key."); - break; - default: - $content->set('message', "The signature you submitted doesn't look valid."); - } - } - } elseif(isset($_POST['delete_signature']) && ($user_is_owner || $active_user->admin)) { - foreach($signatures as $sig) { - if($sig->id == $_POST['delete_signature']) { - $sig_to_delete = $sig; - } - } - if(isset($sig_to_delete)) { - $pubkey->delete_signature($sig_to_delete); - } - redirect('#sig'); - } elseif(isset($_POST['add_dest_rule']) && ($user_is_owner || $active_user->admin)) { - $rule = new PublicKeyDestRule; - $rule->account_name_filter = $_POST['account_name_filter']; - $rule->hostname_filter = $_POST['hostname_filter']; - $pubkey->add_destination_rule($rule); - redirect('#dest'); - } elseif(isset($_POST['delete_dest_rule']) && ($user_is_owner || $active_user->admin)) { - foreach($dest_rules as $rule) { - if($rule->id == $_POST['delete_dest_rule']) { - $rule_to_delete = $rule; - } - } - if(isset($rule_to_delete)) { - $pubkey->delete_destination_rule($rule_to_delete); - } - redirect('#dest'); - } else { - $content = new PageSection('pubkey'); - $content->set('pubkey', $pubkey); - $content->set('admin', $active_user->admin); - $content->set('user_is_owner', $user_is_owner); - $content->set('signatures', $signatures); - $content->set('dest_rules', $dest_rules); - } - $head = ''."\n"; - $head .= ''."\n"; - $page = new PageSection('base'); - $page->set('title', $title); - $page->set('head', $head); - $page->set('content', $content); - $page->set('alerts', $active_user->pop_alerts()); - echo $page->generate(); -} diff --git a/views/pubkeys.php b/views/pubkeys.php deleted file mode 100644 index 38998f3..0000000 --- a/views/pubkeys.php +++ /dev/null @@ -1,26 +0,0 @@ -list_public_keys(array(), $filter); - -if(isset($router->vars['format']) && $router->vars['format'] == 'json') { - $page = new PageSection('pubkeys_json'); - $page->set('pubkeys', $pubkeys); - header('Content-type: text/plain; charset=utf-8'); - echo $page->generate(); -} else { - $content = new PageSection('pubkeys'); - $content->set('filter', $filter); - $content->set('pubkeys', $pubkeys); - $content->set('admin', $active_user->admin); - - $page = new PageSection('base'); - $page->set('title', 'Public keys'); - $page->set('content', $content); - $page->set('alerts', $active_user->pop_alerts()); - echo $page->generate(); -} diff --git a/views/server.php b/views/server.php index 6124d6a..b63055a 100644 --- a/views/server.php +++ b/views/server.php @@ -48,29 +48,6 @@ $server->delete_admin($admin_to_delete); } redirect('#admins'); -} elseif(isset($_POST['add_account']) && ($server_admin || $active_user->admin)) { - $account = new ServerAccount(); - $account->name = trim($_POST['account_name']); - try { - $server->add_account($account); - } catch(AccountNameInvalid $e) { - $alert = new UserAlert; - $alert->content = $e->getMessage(); - $alert->class = 'danger'; - $active_user->add_alert($alert); - } - redirect('#accounts'); -} elseif(isset($_POST['delete_account']) && ($server_admin || $active_user->admin)) { - foreach($server_accounts as $account) { - if($account->id == $_POST['delete_account']) { - $account_to_delete = $account; - } - } - if(isset($account_to_delete)) { - $account_to_delete->active = 0; - $account_to_delete->update(); - } - redirect('#accounts'); } elseif(isset($_POST['edit_server']) && $active_user->admin) { $hostname = trim($_POST['hostname']); if(!preg_match('|.*\..*\..*|', $hostname)) { @@ -113,62 +90,6 @@ if($_POST['rsa_key_fingerprint'] == '') $server->rsa_key_fingerprint = null; $server->update(); redirect('#settings'); -} elseif(isset($_POST['request_access'])) { - // Where we are requesting access FROM - switch($_POST['request_access']) { - case 'user': - $from = $active_user; - $from_description = ''; - break; - case 'server_account': - try { - $server_remote = $server_dir->get_server_by_hostname($_POST['hostname_remote']); - $from = $server_remote->get_account_by_name($_POST['account_remote']); - $from_description = " from {$from->name}@{$server_remote->hostname}"; - } catch(ServerNotFoundException $e) { - $content = new PageSection('server_not_found'); - } catch(ServerAccountNotFoundException $e) { - $content = new PageSection('server_account_not_found'); - } - break; - case 'group': - try { - $from = $group_dir->get_group_by_name($_POST['group_account']); - $from_description = " from group: {$from->name}"; - } catch(GroupNotFoundException $e) { - $content = new PageSection('group_not_found'); - } - break; - default: - throw new Exception("Unrecognized access request type: {$_POST['request_access']}"); - } - // Where we are requesting access TO - $account_name = trim($_POST['account_name']); - try { - $account = $server->get_account_by_name($account_name); - } catch(ServerAccountNotFoundException $e) { - $account = new ServerAccount; - $account->name = trim($account_name); - $account->sync_status = 'proposed'; - try { - $server->add_account($account); - } catch(AccountNameInvalid $e) { - $alert = new UserAlert; - $alert->content = $e->getMessage(); - $alert->class = 'danger'; - $active_user->add_alert($alert); - redirect(); - } - } - // Add access request if we found everything - if(isset($from) && isset($account)) { - $account->add_access_request($from); - - $alert = new UserAlert; - $alert->content = "Access requested to {$account->name}@{$server->hostname}{$from_description}."; - $active_user->add_alert($alert); - redirect(); - } } elseif(isset($_POST['add_note']) && $active_user->admin) { $note = new ServerNote(); $note->note = $_POST['note']; @@ -204,27 +125,6 @@ } } break; - case 'root_users': - try { - $account = $server->get_account_by_name('root'); - } catch(ServerAccountNotFoundException $e) { - $alert = new UserAlert; - $alert->content = "Could not send mail: root account does not exist on this server."; - $alert->class = 'danger'; - $active_user->add_alert($alert); - redirect(); - } - foreach($account->list_access() as $access) { - $entity = $access->source_entity; - if(get_class($entity) == 'User' && $entity->active) { - if($hide_recipients) { - $email->add_bcc($entity->email, $entity->name); - } else { - $email->add_recipient($entity->email, $entity->name); - } - } - } - break; case 'users': $users = array(); foreach($server_accounts as $account) { diff --git a/views/serveraccount.php b/views/serveraccount.php deleted file mode 100644 index fc9cf00..0000000 --- a/views/serveraccount.php +++ /dev/null @@ -1,192 +0,0 @@ -get_server_by_hostname($router->vars['hostname']); - $server_admin = $active_user->admin_of($server); - $account_admin = false; - if(!$server_admin && !$active_user->admin) { - try { - $account = $server->get_account_by_name($router->vars['account']); - $account_admin = $active_user->admin_of($account); - } catch(ServerAccountNotFoundException $e) { - } - if(!$account_admin) { - require('views/error403.php'); - die; - } - } else { - $account = $server->get_account_by_name($router->vars['account']); - } -} catch(ServerNotFoundException $e) { - require('views/error404.php'); - die; -} catch(ServerAccountNotFoundException $e) { - require('views/error404.php'); - die; -} -$account_access = $account->list_access(); -$account_access_requests = $account->list_access_requests(); -$account_remote_access = $account->list_remote_access(); -$account_groups = $account->list_group_membership(); -$account_admins = $account->list_admins(); -$pubkeys = $account->list_public_keys(); -if(isset($_POST['add_access']) && ($server_admin || $account_admin || $active_user->admin)) { - if(isset($_POST['username'])) { - try { - $entity = $user_dir->get_user_by_uid(trim($_POST['username'])); - } catch(UserNotFoundException $e) { - $content = new PageSection('user_not_found'); - } - } elseif(isset($_POST['account'])) { - try { - $remoteserver = $server_dir->get_server_by_hostname(trim($_POST['hostname'])); - $entity = $remoteserver->get_account_by_name(trim($_POST['account'])); - } catch(ServerNotFoundException $e) { - $content = new PageSection('server_not_found'); - } catch(ServerAccountNotFoundException $e) { - $content = new PageSection('server_account_not_found'); - } - } elseif(isset($_POST['group'])) { - try { - $entity = $group_dir->get_group_by_name(trim($_POST['group'])); - } catch(GroupNotFoundException $e) { - $content = new PageSection('group_not_found'); - } - } - if(isset($entity)) { - if($_POST['add_access'] == '2') { - $options = array(); - if(isset($_POST['access_option'])) { - foreach($_POST['access_option'] as $k => $v) { - if(isset($v['enabled'])) { - $option = new AccessOption(); - $option->option = $k; - if(isset($v['value'])) { - $option->value = $v['value']; - } else { - $option->value = null; - } - $options[] = $option; - } - } - } - $account->add_access($entity, $options); - redirect('#access'); - } else { - $content = new PageSection('access_options'); - $content->set('entity', $account); - $content->set('remote_entity', $entity); - $content->set('mode', 'create'); - } - } -} elseif(isset($_POST['delete_access']) && ($server_admin || $account_admin || $active_user->admin)) { - foreach($account_access as $access) { - if($access->id == $_POST['delete_access']) { - $access_to_delete = $access; - } - } - if(isset($access_to_delete)) { - $account->delete_access($access_to_delete); - } - redirect('#access'); -} elseif(isset($_POST['approve_access']) && ($server_admin || $account_admin || $active_user->admin)) { - foreach($account_access_requests as $request) { - if($request->id == $_POST['approve_access']) { - $request_to_approve = $request; - } - } - if(isset($request_to_approve)) { - $account->approve_access_request($request_to_approve); - redirect('#access'); - } -} elseif(isset($_POST['reject_access']) && ($server_admin || $account_admin || $active_user->admin)) { - foreach($account_access_requests as $request) { - if($request->id == $_POST['reject_access']) { - $request_to_reject = $request; - } - } - if(isset($request_to_reject)) { - $sync_status = $account->sync_status; - $account->reject_access_request($request_to_reject); - // Check to see if account still exists - try { - $account = $server->get_account_by_name($router->vars['account']); - redirect('#access'); - } catch(ServerAccountNotFoundException $e) { - redirect('/servers/'.urlencode($server->hostname).'#accounts'); - } - } -} elseif(isset($_POST['add_public_key']) && ($server_admin || $account_admin || $active_user->admin)) { - try { - $public_key = new PublicKey; - $public_key->import($_POST['add_public_key'], null, isset($_POST['force']) && $active_user->admin); - $account->add_public_key($public_key); - redirect('#pubkeys'); - } catch(InvalidArgumentException $e) { - $content = new PageSection('key_upload_fail'); - switch($e->getMessage()) { - case 'Insufficient bits in public key': - $minbits_rsa = $config['general']['minimum_rsa_key_size']; - $minbits_ecdsa = $config['general']['minimum_ecdsa_key_size']; - $content->set('message', "The public key you submitted is of insufficient strength; it must be at least " . $minbits_rsa . " bits for rsa and " . $minbits_ecdsa . " for ecdsa."); - break; - default: - $content->set('message', "The public key you submitted doesn't look valid."); - } - } catch(PublicKeyAlreadyKnownException $e) { - $content = new PageSection('key_upload_fail'); - $content->set('message', "The public key you submitted is already in use. Please create a new one."); - } -} elseif(isset($_POST['delete_public_key']) && ($server_admin || $account_admin || $active_user->admin)) { - foreach($pubkeys as $pubkey) { - if($pubkey->id == $_POST['delete_public_key']) { - $key_to_delete = $pubkey; - } - } - if(isset($key_to_delete)) { - $account->delete_public_key($key_to_delete); - } - redirect('#pubkeys'); -} elseif(isset($_POST['add_admin']) && ($server_admin || $active_user->admin)) { - try { - $user = $user_dir->get_user_by_uid($_POST['user_name']); - } catch(UserNotFoundException $e) { - $content = new PageSection('user_not_found'); - } - if(isset($user)) { - $account->add_admin($user); - redirect('#admins'); - } -} elseif(isset($_POST['delete_admin']) && ($server_admin || $active_user->admin)) { - foreach($account_admins as $admin) { - if($admin->id == $_POST['delete_admin']) { - $admin_to_delete = $admin; - } - } - if(isset($admin_to_delete)) { - $account->delete_admin($admin_to_delete); - } - redirect('#admins'); -} else { - $content = new PageSection('serveraccount'); - $content->set('server', $server); - $content->set('account', $account); - $content->set('access', $account_access); - $content->set('access_requests', $account_access_requests); - $content->set('pubkeys', $pubkeys); - $content->set('remote_access', $account_remote_access); - $content->set('group_membership', $account_groups); - $content->set('admins', $account_admins); - $content->set('admin', $active_user->admin); - $content->set('log', $account->get_log()); - $content->set('server_admin', $server_admin); - $content->set('all_users', $user_dir->list_users()); - $content->set('all_servers', $server_dir->list_servers()); - $content->set('all_groups', $group_dir->list_groups()); -} - -$page = new PageSection('base'); -$page->set('title', $account->name.'@'.$server->hostname); -$page->set('content', $content); -$page->set('alerts', $active_user->pop_alerts()); -echo $page->generate(); diff --git a/views/serveraccount_pubkeys.php b/views/serveraccount_pubkeys.php deleted file mode 100644 index 6919d0c..0000000 --- a/views/serveraccount_pubkeys.php +++ /dev/null @@ -1,23 +0,0 @@ -get_server_by_hostname($router->vars['hostname']); - $account = $server->get_account_by_name($router->vars['account']); -} catch(ServerAccountNotFoundException $e) { - require('views/error404.php'); - die; -} catch(ServerNotFoundException $e) { - require('views/error404.php'); - die; -} -$pubkeys = $account->list_public_keys(); -if(isset($router->vars['format']) && $router->vars['format'] == 'txt') { - $page = new PageSection('entity_pubkeys_txt'); - $page->set('pubkeys', $pubkeys); - header('Content-type: text/plain; charset=utf-8'); - echo $page->generate(); -} elseif(isset($router->vars['format']) && $router->vars['format'] == 'json') { - $page = new PageSection('entity_pubkeys_json'); - $page->set('pubkeys', $pubkeys); - header('Content-type: application/json; charset=utf-8'); - echo $page->generate(); -} diff --git a/views/serveraccount_sync_status.php b/views/serveraccount_sync_status.php deleted file mode 100644 index bb1ef19..0000000 --- a/views/serveraccount_sync_status.php +++ /dev/null @@ -1,16 +0,0 @@ -get_server_by_hostname($router->vars['hostname']); - $account = $server->get_account_by_name($router->vars['account']); -} catch(ServerAccountNotFoundException $e) { - require('views/error404.php'); - die; -} catch(ServerNotFoundException $e) { - require('views/error404.php'); - die; -} -$page = new PageSection('serveraccount_sync_status_json'); -$page->set('sync_status', $account->sync_status); -$page->set('pending', $account->sync_is_pending()); -header('Content-type: application/json; charset=utf-8'); -echo $page->generate(); diff --git a/views/servers.php b/views/servers.php index 0972d04..e42399e 100644 --- a/views/servers.php +++ b/views/servers.php @@ -47,7 +47,7 @@ $active_user->add_alert($alert); } catch(ServerAlreadyExistsException $e) { $alert = new UserAlert; - $alert->content = 'Server \''.hesc($hostname).'\' is already known by SSH Key Authority.'; + $alert->content = 'Server \''.hesc($hostname).'\' is already known by SSL Cert Authority.'; $alert->escaping = ESC_NONE; $alert->class = 'danger'; $active_user->add_alert($alert); @@ -89,10 +89,10 @@ $content->set('servers', $servers); $content->set('all_users', $user_dir->list_users()); $content->set('all_groups', $group_dir->list_groups()); - if(file_exists('config/keys-sync.pub')) { - $content->set('keys-sync-pubkey', file_get_contents('config/keys-sync.pub')); + if(file_exists('config/cert-sync.pub')) { + $content->set('cert-sync-pubkey', file_get_contents('config/cert-sync.pub')); } else { - $content->set('keys-sync-pubkey', 'Error: keyfile missing'); + $content->set('cert-sync-pubkey', 'Error: keyfile missing'); } } } diff --git a/views/user.php b/views/user.php index cef2349..6a8c617 100644 --- a/views/user.php +++ b/views/user.php @@ -38,7 +38,7 @@ $user->update(); redirect('#settings'); } elseif(isset($_POST['delete_user']) && $active_user->admin) { - if($user->auth_realm == 'local' && $user->uid != 'keys-sync' ) { + if($user->auth_realm == 'local' && $user->uid != 'cert-sync' ) { $user->delete(); } redirect('/users'); diff --git a/views/users.php b/views/users.php index 0d6b5a1..59ccbf9 100644 --- a/views/users.php +++ b/views/users.php @@ -25,7 +25,7 @@ $active_user->add_alert($alert); } catch(UserAlreadyExistsException $e) { $alert = new UserAlert; - $alert->content = 'User \''.hesc($user->uid).'\' is already known by SSH Key Authority.'; + $alert->content = 'User \''.hesc($user->uid).'\' is already known by SSL Cert Authority.'; $alert->escaping = ESC_NONE; $alert->class = 'danger'; $active_user->add_alert($alert);