summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2017-07-18 03:35:52 +0000
committerMike Crute <mike@crute.us>2017-07-18 03:35:52 +0000
commit72f7d25a69b6ed41a15eef973f9fbb821931c901 (patch)
treefb6c7e60e273c7a471c10c4ab6236a58f1efe2d2
downloadpy_openid_connect-72f7d25a69b6ed41a15eef973f9fbb821931c901.tar.bz2
py_openid_connect-72f7d25a69b6ed41a15eef973f9fbb821931c901.tar.xz
py_openid_connect-72f7d25a69b6ed41a15eef973f9fbb821931c901.zip
Initial importHEADmaster
-rwxr-xr-xmanage.py64
-rw-r--r--server.py87
-rw-r--r--var/ssl-cert.pem17
-rw-r--r--var/ssl-key.pem28
4 files changed, 196 insertions, 0 deletions
diff --git a/manage.py b/manage.py
new file mode 100755
index 0000000..8838a1e
--- /dev/null
+++ b/manage.py
@@ -0,0 +1,64 @@
1#!/usr/bin/env python
2
3import os
4import sys
5import socket
6from OpenSSL import crypto
7from OpenSSL.crypto import PKey, X509
8
9from server import app
10
11
12VAR_PATH = "var"
13LOG_PATH = os.path.join(VAR_PATH, "log")
14KEY_PATH = os.path.join(VAR_PATH, "ssl-key.pem")
15CERT_PATH = os.path.join(VAR_PATH, "ssl-cert.pem")
16
17
18def generate_cert(serial=1):
19 ca = X509()
20 ca.set_version(2)
21 ca.set_serial_number(serial)
22 ca.get_subject().CN = socket.getfqdn()
23 ca.gmtime_adj_notBefore(0)
24 ca.gmtime_adj_notAfter(24 * 60 * 60)
25 ca.set_issuer(ca.get_subject())
26 ca.set_pubkey(key)
27 ca.sign(key, "sha256")
28
29 with open(CERT_PATH, "wb") as fp:
30 fp.write(crypto.dump_certificate(crypto.FILETYPE_PEM, ca))
31
32
33if not os.path.exists(VAR_PATH):
34 os.mkdir(VAR_PATH)
35
36if not os.path.exists(LOG_PATH):
37 os.mkdir(LOG_PATH)
38
39if not os.path.exists(KEY_PATH):
40 key = PKey()
41 key.generate_key(crypto.TYPE_RSA, 2048)
42
43 with open(KEY_PATH, "wb") as fp:
44 os.chmod(KEY_PATH, 0o600)
45 fp.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key))
46else:
47 with open(KEY_PATH, "rb") as fp:
48 key = crypto.load_privatekey(crypto.FILETYPE_PEM, fp.read())
49
50
51if not os.path.exists(CERT_PATH):
52 generate_cert()
53else:
54 with open(CERT_PATH, "rb") as fp:
55 cert = crypto.load_certificate(crypto.FILETYPE_PEM, fp.read())
56
57 if cert.has_expired():
58 generate_cert(cert.get_serial_number() + 1)
59
60
61if __name__ == "__main__" and sys.argv[-1] == "runserver":
62 app.run(host="0.0.0.0", port=5000,
63 ssl_context=("var/ssl-cert.pem", "var/ssl-key.pem"),
64 threaded=True, debug=True)
diff --git a/server.py b/server.py
new file mode 100644
index 0000000..0bebe2a
--- /dev/null
+++ b/server.py
@@ -0,0 +1,87 @@
1import flask
2import urllib.parse
3from flask import request
4
5
6# Application Config
7OIDC_ISSUER = "https://id.crute.me/"
8OIDC_AUTH_ENDPOINT = "{}login".format(OIDC_ISSUER)
9OIDC_JWKS_ENDPOINT = "{}jwks".format(OIDC_ISSUER)
10DOMAIN_WHITELIST = ["crute.me", "crute.us"]
11
12
13# Generic Constants
14JRD_MIMETYPE = "application/jrd+json"
15OIDC_ISSUER_QUERY = "http://openid.net/specs/connect/1.0/issuer"
16JRD_404 = flask.Response("Not found", status=404, mimetype=JRD_MIMETYPE)
17
18
19app = flask.Flask(__name__)
20
21
22def parse_email_addr(addr):
23 try:
24 user, domain = addr.split("@")
25 return user, domain
26 except ValueError:
27 return None
28
29
30@app.route("/.well-known/webfinger", methods=["GET"])
31def webfinger():
32 resource = request.args.get("resource")
33 rel = request.args.get("rel")
34
35 # Only support OIDC queries
36 if not rel or not resource or not rel == OIDC_ISSUER_QUERY:
37 return JRD_404
38
39 # Only support email address queries
40 acct = urllib.parse.urlparse(resource)
41 if acct.scheme != "acct":
42 return JRD_404
43
44 # Ensure that the query is in a whitelisted domain
45 _, domain = parse_email_addr(acct.path)
46 if domain not in DOMAIN_WHITELIST:
47 return JRD_404
48
49 # We don't validate that the user exists to prevent leaking information
50 # about what users exist; plus, it doesn't really matter since the querier
51 # only cares about the OIDC issuer endpoint which is user independent
52 res = flask.jsonify({
53 "subject": resource,
54 "links": [{
55 "rel": OIDC_ISSUER_QUERY,
56 "href": OIDC_ISSUER,
57 }]
58 })
59 res.mimetype = JRD_MIMETYPE
60 return res
61
62
63
64@app.route("/.well-known/openid-configuration", methods=["GET"])
65def openid_configuration():
66 return flask.jsonify({
67 "issuer": OIDC_ISSUER,
68 "authorization_endpoint": OIDC_AUTH_ENDPOINT,
69 "jwks_uri": OIDC_JWKS_ENDPOINT,
70 "scopes_supported": ["openid"],
71 "response_types_supported": ["id_token"],
72 "response_modes_supported": ["query"],
73 "grant_types_supported": ["implicit"],
74 "subject_types_supported": ["public"],
75 "id_token_signing_alg_values_supported": ["RS256"],
76 "acr_values_supported": ["want_mfa", "need_mfa"],
77 })
78
79
80@app.route("/login", methods=["GET", "POST"])
81def login():
82 pass
83
84
85@app.route("/jwks", methods=["GET"])
86def keys():
87 pass
diff --git a/var/ssl-cert.pem b/var/ssl-cert.pem
new file mode 100644
index 0000000..207aea1
--- /dev/null
+++ b/var/ssl-cert.pem
@@ -0,0 +1,17 @@
1-----BEGIN CERTIFICATE-----
2MIICwTCCAamgAwIBAgIBATANBgkqhkiG9w0BAQsFADAkMSIwIAYDVQQDDBltY3J1
3dGUtdmlydC5zZWExLmNydXRlLm1lMB4XDTE3MDIyNTAzNTAwNloXDTE3MDIyNjAz
4NTAwNlowJDEiMCAGA1UEAwwZbWNydXRlLXZpcnQuc2VhMS5jcnV0ZS5tZTCCASIw
5DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKZqUzkQdVMdNX0ozFb1BF2SXJao
6oDHs1gz4yvNTpfCMVKl+DIpb6QOkfEL4UOs2MSRGm0we2Va7e87gnmRGdmjLuSQQ
7SDEVCnGH5eqL9WqqZzMB0TJsHm1saeJakGePVeDiJMlRFxCjS/8TXBkRP0LJge6i
8TQQ0ZVbAVxEvmuGRdPIIQGqBifKm6zaGszD992xARt8vYDiAlGKEssmLnrTpZ1kp
9dc/r+tKe+FHWVLggEvP9xkWVmTsABGd00CvYfI/716BZrG7dpQrRfMGFOcq3lpPA
10uIp+uHaOVmpT85GlJl/LM6qbnjh3xL9iJFFgdIWVZD6USlWTlVaFm1l8os8CAwEA
11ATANBgkqhkiG9w0BAQsFAAOCAQEAnwpJAp2TM7EeW/o3656dmmr35A5txEpCMJ16
12Mb5izuj67lrgcCqGltFRxZSyFRDii3/BN2SMGt2Uhtt/cctDJwU1Bl3Tt4WfsGgc
13pLARspitlVEbpeOwoZdXj1m6ILHuQ2qsuTNBwKqW8ZmeaxKodPDWB9L9tCWypJSz
14GvWAOvTqVqDJ3ob+UcCNHBH2on8Y2K14fCqcTsEFQs3vcPKfqBPKGs8xv7cZ+mrl
15Wh++JI8GprNcPMF6owMGKhwVn/bXmDPXIb67OM05PvlfmbRs8DQEzzz5oQj7yLY7
16iI7zu/OBHprWeUvQcvZcWzWaeVp2a89X4t9O3ODa5QZmDELHNw==
17-----END CERTIFICATE-----
diff --git a/var/ssl-key.pem b/var/ssl-key.pem
new file mode 100644
index 0000000..7cd8796
--- /dev/null
+++ b/var/ssl-key.pem
@@ -0,0 +1,28 @@
1-----BEGIN PRIVATE KEY-----
2MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCmalM5EHVTHTV9
3KMxW9QRdklyWqKAx7NYM+MrzU6XwjFSpfgyKW+kDpHxC+FDrNjEkRptMHtlWu3vO
44J5kRnZoy7kkEEgxFQpxh+Xqi/VqqmczAdEybB5tbGniWpBnj1Xg4iTJURcQo0v/
5E1wZET9CyYHuok0ENGVWwFcRL5rhkXTyCEBqgYnypus2hrMw/fdsQEbfL2A4gJRi
6hLLJi5606WdZKXXP6/rSnvhR1lS4IBLz/cZFlZk7AARndNAr2HyP+9egWaxu3aUK
70XzBhTnKt5aTwLiKfrh2jlZqU/ORpSZfyzOqm544d8S/YiRRYHSFlWQ+lEpVk5VW
8hZtZfKLPAgMBAAECggEAcMlFCDrYWXFFbEM3YoQC5mwo5k74631JgVcpLYr2vzZM
9tubgFvG91iMnuLfVF+UNnzlfTVLnGDpO6eIgV3POEq5oF0IVu3Y4MsPZFoKu3REK
10im5j2xmd8al1hdy9XAKwQI7kQbD8weD6w7DaTX778gbiUdqb+gqM2CPZnqM2BEPZ
118mDyQ9YteDykbC/ZWDEnorb35F9zKp4BFBOgLpRm7Ylj9VYqYxutj94fAA8juKyu
12cB8XntnDZE40VSY6hPhivmF3Bi8OcHBnKNuVW/+Cg8SqbvuxeMRN+lgV8AcRvQFR
13eFE0WRo0uEpkwsoFwXW5jdOyPYzeNJNLcjKBNhdz6QKBgQDYwBdQZIW3iGLjiEtY
14j5naThzm2QyIcV5TolIOE2iFT0c7ZpM2r9k5iWGbN1BVJYp3A4WI4Pf9TdpA3upQ
15nGnm7e2TF2pbZmb7dNCV+8SkHxqE0TmZAfX2zHVYa62Ak+4/CaN45CR+3NLpmc7E
164A9JqshcLyJpIgJpXohpSa3KkwKBgQDEjNmzcxpDZson3v1wVfRpREE/+ImztwMb
17wQeMO4IdQ8PVcaTS/9Tfo72ttjJEQJKJWOQK/DrJJB3tbTfv7OxXW+9g40uiNb4y
18P/1Zj9osAWQ9US5SR2Diu2TJf+RnLOONHDFlolp6h2ODp6eZitRedVrlNOhIKjRE
190X6YvHIgVQKBgHhmG404iV5tkCC7sL685cVx5nQE1QVlk/P5EoNnHIQQiYzhaZzT
20CWMAgQtrKmBhvgxmFGL4NEisWjP2n0mv2NPCDlnKg/XJaCL9vG/TrmNMM7rbTZdE
21k5BH+lbnWTU29vxlKj0Y3XzqxO2l1kld/J/7EdjDBeZIUfad4EA8ASGpAoGAcLbR
22VY5pfkOhJJJFRrmUKxXgUV4nIa+PEch1mp65tCybTnkYa4QYwJN2//pfAzMAldEr
23HX2d/KFApFqg3G9C7aokMazHP+OQCeMWV9kd5WR65or6hGa4ke2jE8bK5bwhVlfX
24oBAl0OR5VhO8ElpCBVVJZe4cUt4ZEWoOLmrLC+0CgYBLDlBZsvfE1x4UEn4LFLQC
25LnNEjFvYbtKXP4Tm5Kk3FhDUMoBkwm69AhXgfq+ddCXUrBjsMaYZhdi2FPqa5qEh
26K6RPu42yIElN50UnabRkZbShCUEEeioOGTjAjhyd77O9ol9C7M57L38OnEs8MmRa
27gvVNvuCVuHjcnd06dDTWPw==
28-----END PRIVATE KEY-----