templates_people_modulesdir = $(templates_peopledir)/modules
-templates_people_ssh_keys_DATA = \
- src/templates/people/ssh-keys/delete.html \
- src/templates/people/ssh-keys/error.html \
- src/templates/people/ssh-keys/error-invalid-key.html \
- src/templates/people/ssh-keys/index.html \
- src/templates/people/ssh-keys/upload.html
-
-templates_people_ssh_keysdir = $(templates_peopledir)/ssh-keys
-
templates_static_DATA = \
src/templates/static/blocked.html \
src/templates/static/chat.html \
requests-oauthlib==1.2.0
sgmllib3k==1.0.0
six==1.11.0
-sshpubkeys==3.1.0
textile==3.0.3
tornado==6.0.2
twython==3.7.0
import os
import phonenumbers
import re
-import sshpubkeys
import time
import tornado.httpclient
import urllib.parse
def upload_avatar(self, avatar):
self._set("jpegPhoto", avatar)
- # SSH Keys
-
- @lazy_property
- def ssh_keys(self):
- ret = []
-
- for key in self._get_strings("sshPublicKey"):
- s = sshpubkeys.SSHKey()
-
- try:
- s.parse(key)
- except (sshpubkeys.InvalidKeyError, NotImplementedError) as e:
- logging.warning("Could not parse SSH key %s: %s" % (key, e))
- continue
-
- ret.append(s)
-
- return ret
-
- def get_ssh_key_by_hash_sha256(self, hash_sha256):
- for key in self.ssh_keys:
- if not key.hash_sha256() == hash_sha256:
- continue
-
- return key
-
- def add_ssh_key(self, key):
- k = sshpubkeys.SSHKey()
-
- # Try to parse the key
- k.parse(key)
-
- # Check for types and sufficient sizes
- if k.key_type == b"ssh-rsa":
- if k.bits < 4096:
- raise sshpubkeys.TooShortKeyError("RSA keys cannot be smaller than 4096 bits")
-
- elif k.key_type == b"ssh-dss":
- raise sshpubkeys.InvalidKeyError("DSA keys are not supported")
-
- # Ignore any duplicates
- if key in (k.keydata for k in self.ssh_keys):
- logging.debug("SSH Key has already been added for %s: %s" % (self, key))
- return
-
- # Prepare transaction
- modlist = []
-
- # Add object class if user is not in it, yet
- if not "ldapPublicKey" in self.classes:
- modlist.append((ldap.MOD_ADD, "objectClass", b"ldapPublicKey"))
-
- # Add key
- modlist.append((ldap.MOD_ADD, "sshPublicKey", key.encode()))
-
- # Save key to LDAP
- self._modify(modlist)
-
- # Append to cache
- self.ssh_keys.append(k)
-
- def delete_ssh_key(self, key):
- if not key in (k.keydata for k in self.ssh_keys):
- return
-
- # Delete key from LDAP
- if len(self.ssh_keys) > 1:
- self._delete_string("sshPublicKey", key)
- else:
- self._modify([
- (ldap.MOD_DELETE, "objectClass", b"ldapPublicKey"),
- (ldap.MOD_DELETE, "sshPublicKey", key.encode()),
- ])
-
class StopForumSpam(Object):
def init(self, uid, email, address):
<a class="btn btn-light btn-sm btn-block" href="/~{{ account.uid }}/">
<span class="fas fa-home mr-2"></span> {{ _("Home Directory") }}
</a>
-
- <a class="btn btn-light btn-sm btn-block" href="/users/{{ account.uid }}/ssh-keys">
- <span class="fas fa-key mr-2"></span> {{ _("SSH Keys") }}
- </a>
{% end %}
{% if account.has_sip() %}
+++ /dev/null
-{% extends "../base.html" %}
-
-{% block title %}{{ account }} - {{ _("Delete SSH Key") }}{% end block %}
-
-{% block main %}
- <div class="row justify-content-center">
- <div class="col col-md-8">
- <h4 class="mb-4">{{ _("Delete SSH Key") }}</h4>
-
- <form method="POST" action="">
- {% raw xsrf_form_html() %}
-
- <div class="form-group">
- <label>{{ _("Fingerprints") }}</label>
-
- {% for h in (key.hash_md5(), key.hash_sha256(), key.hash_sha512()) %}
- <p class="text-monospace small text-truncate mb-0">
- {{ h }}
- </p>
- {% end %}
- </div>
-
- <div class="form-group">
- <label>{{ _("Current Password") }}</label>
-
- <input type="password" class="form-control" name="password"
- placeholder="{{ _("Current Password") }}">
-
- <small class="form-text text-muted">
- {{ _("To authorize uploading a new SSH key, your password is required") }}
- </small>
- </div>
-
- <input class="btn btn-primary btn-block" type="submit" value="{{ _("Delete SSH Key") }}">
- </form>
- </div>
- </div>
-{% end block %}
+++ /dev/null
-{% extends "error.html" %}
-
-{% block reason %}
- <p class="card-text">
- {{ _("The uploaded key is invalid: %s") % exception }}
- </p>
-{% end block %}
+++ /dev/null
-{% extends "../base.html" %}
-
-{% block title %}{{ account }} - {{ _("Error Adding SSH Key") }}{% end block %}
-
-{% block main %}
- <h1 class="mb-0">{{ _("Error") }}</h1>
- <h6>{{ _("Your SSH Key could not be added") }}</h6>
-
- <div class="card card-body bg-danger text-white">
- {% block reason %}{% end block %}
-
- <p class="card-text">
- {{ _("Please go back and try again") }}
- </p>
- </div>
-{% end block %}
+++ /dev/null
-{% extends "../base.html" %}
-
-{% block title %}{{ account }} - {{ _("SSH Keys") }}{% end block %}
-
-{% block main %}
- <h1>{{ _("SSH Keys") }}</h1>
-
- <ul class="list-group mb-3">
- {% for key in account.ssh_keys %}
- <li class="list-group-item">
- <h5 class="mb-1">
- <a class="text-dark" href="/users/{{ account.uid }}/ssh-keys/{{ key.hash_sha256() }}">
- {{ key.comment or _("%s Key") % key.key_type.decode() }}
- </a>
- </h5>
-
- <p class="small text-muted mb-3">
- {% if key.key_type == b"ssh-rsa" %}
- {{ _("RSA") }}
- {% elif key.key_type.startswith(b"ecdsa-sha") %}
- {{ _("ECDSA") }}
- {% elif key.key_type == b"ssh-ed25519" %}
- {{ _("Ed25519") }}
- {% else %}
- {{ key.key_type.decode() }}
- {% end %}
-
- {{ _("%s Bit") % key.bits }}
- </p>
-
- <p class="text-monospace small text-truncate">
- {% for h in (key.hash_md5(), key.hash_sha256(), key.hash_sha512()) %}
- {{ h }}<br>
- {% end %}
- </p>
-
- {% if key.options %}
- <p class="my-1">{{ _("Options") }}</p>
-
- <ul class="mb-0">
- {% for option, values in sorted(key.options.items()) %}
- <li class="small">
- <span class="text-monospace">{{ option }}</span>
-
- {% if not values == [True] %}
- = <span class="text-monospace">{{ "".join(values) }}</span>
- {% end %}
- </li>
- {% end %}
- </ul>
- {% end %}
-
- {% if account.can_be_managed_by(current_user) %}
- <a class="btn btn-outline-danger btn-sm btn-block mt-2" href="/users/{{ account.uid }}/ssh-keys/{{ key.hash_sha256() }}/delete">
- {{ _("Delete") }}
- </a>
- {% end %}
- </li>
- {% end %}
- </ul>
-
- {% if account.can_be_managed_by(current_user) %}
- <a class="btn btn-success btn-block" href="/users/{{ account.uid }}/ssh-keys/upload">
- {{ _("Upload New SSH Key") }}
- </a>
- {% end %}
-{% end block %}
+++ /dev/null
-{% extends "../base.html" %}
-
-{% block title %}{{ account }} - {{ _("Upload New SSH Key") }}{% end block %}
-
-{% block main %}
- <div class="row justify-content-center">
- <div class="col col-md-8">
- <h4 class="mb-4">{{ _("Upload New SSH Key") }}</h4>
-
- <form method="POST" action="">
- {% raw xsrf_form_html() %}
-
- <div class="form-group">
- <textarea class="form-control" name="key" rows="3" required
- placeholder="{{ _("SSH Key") }}"></textarea>
-
- <small class="form-text text-muted">
- {{ _("The SSH key must be conforming to these criteria:") }}
-
- <ul>
- <li>{{ _("Supported key types are: Ed25519, ECDSA and RSA") }}</li>
- <li>{{ _("RSA keys must be at least 4096 bits long") }}</li>
- </ul>
- </small>
- </div>
-
- <div class="form-group">
- <label>{{ _("Current Password") }}</label>
-
- <input type="password" class="form-control" name="password"
- placeholder="{{ _("Current Password") }}">
-
- <small class="form-text text-muted">
- {{ _("To authorize uploading a new SSH key, your password is required") }}
- </small>
- </div>
-
- <input class="btn btn-primary btn-block" type="submit" value="{{ _("Upload SSH Key") }}">
- </form>
- </div>
- </div>
-{% end block %}
(r"/users/(\w+)/calls(?:/(\d{4}-\d{2}-\d{2}))?", people.CallsHandler),
(r"/users/(\w+)/edit", people.UserEditHandler),
(r"/users/(\w+)/passwd", people.UserPasswdHandler),
- (r"/users/(\w+)/ssh-keys", people.SSHKeysIndexHandler),
- (r"/users/(\w+)/ssh-keys/(SHA256\:.*)/delete", people.SSHKeysDeleteHandler),
- (r"/users/(\w+)/ssh-keys/(SHA256\:.*)", people.SSHKeysDownloadHandler),
- (r"/users/(\w+)/ssh-keys/upload", people.SSHKeysUploadHandler),
(r"/users/(\w+)/sip", people.SIPHandler),
# Single-Sign-On for Discourse
import ldap
import logging
import imghdr
-import sshpubkeys
import tornado.web
import urllib.parse
self.render("people/search.html", q=q, accounts=accounts)
-class SSHKeysIndexHandler(auth.CacheMixin, base.BaseHandler):
- @tornado.web.authenticated
- def get(self, uid):
- account = self.backend.accounts.get_by_uid(uid)
- if not account:
- raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
-
- self.render("people/ssh-keys/index.html", account=account)
-
-
-class SSHKeysDownloadHandler(auth.CacheMixin, base.BaseHandler):
- @tornado.web.authenticated
- def get(self, uid, hash_sha256):
- account = self.backend.accounts.get_by_uid(uid)
- if not account:
- raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
-
- # Get SSH key
- key = account.get_ssh_key_by_hash_sha256(hash_sha256)
- if not key:
- raise tornado.web.HTTPError(404, "Could not find key: %s" % hash_sha256)
-
- # Set HTTP Headers
- self.add_header("Content-Type", "text/plain")
-
- self.finish(key.keydata)
-
-
-class SSHKeysUploadHandler(auth.CacheMixin, base.BaseHandler):
- @tornado.web.authenticated
- def get(self, uid):
- account = self.backend.accounts.get_by_uid(uid)
- if not account:
- raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
-
- # Check for permissions
- if not account.can_be_managed_by(self.current_user):
- raise tornado.web.HTTPError(403, "%s cannot manage %s" % (self.current_user, account))
-
- self.render("people/ssh-keys/upload.html", account=account)
-
- @tornado.web.authenticated
- def post(self, uid):
- account = self.backend.accounts.get_by_uid(uid)
- if not account:
- raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
-
- # Check for permissions
- if not account.can_be_managed_by(self.current_user):
- raise tornado.web.HTTPError(403, "%s cannot manage %s" % (self.current_user, account))
-
- key = self.get_argument("key")
-
- # Verify password
- password = self.get_argument("password")
- if not account.check_password(password):
- raise tornado.web.HTTPError(403, "Incorrect password for %s" % account)
-
- # Try to add new SSH key
- try:
- account.add_ssh_key(key)
-
- except sshpubkeys.InvalidKeyException as e:
- self.render("people/ssh-keys/error-invalid-key.html", account=account, exception=e)
- return
-
- self.redirect("/users/%s/ssh-keys" % account.uid)
-
-
-class SSHKeysDeleteHandler(auth.CacheMixin, base.BaseHandler):
- @tornado.web.authenticated
- def get(self, uid, hash_sha256):
- account = self.backend.accounts.get_by_uid(uid)
- if not account:
- raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
-
- # Get SSH key
- key = account.get_ssh_key_by_hash_sha256(hash_sha256)
- if not key:
- raise tornado.web.HTTPError(404, "Could not find key: %s" % hash_sha256)
-
- self.render("people/ssh-keys/delete.html", account=account, key=key)
-
- @tornado.web.authenticated
- def post(self, uid, hash_sha256):
- account = self.backend.accounts.get_by_uid(uid)
- if not account:
- raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
-
- # Get SSH key
- key = account.get_ssh_key_by_hash_sha256(hash_sha256)
- if not key:
- raise tornado.web.HTTPError(404, "Could not find key: %s" % hash_sha256)
-
- # Verify password
- password = self.get_argument("password")
- if not account.check_password(password):
- raise tornado.web.HTTPError(403, "Incorrect password for %s" % account)
-
- # Delete the key
- account.delete_ssh_key(key.keydata)
-
- self.redirect("/users/%s/ssh-keys" % account.uid)
-
-
class SIPHandler(auth.CacheMixin, base.BaseHandler):
@tornado.web.authenticated
def get(self, uid):