Add ssl/client/i/dn resource. Closes #113
This commit is contained in:
@@ -43,6 +43,10 @@ Overview
|
||||
│ │ └──── content The contents of the file uploaded in the form field <name>
|
||||
│ └──── body HTTP request body
|
||||
│
|
||||
|─ ssl
|
||||
│ └──── client
|
||||
│ └──── i
|
||||
│ └──── dn Subject's DN common name coming ina request through SSL with mTLS
|
||||
│─ route
|
||||
│ └──── id Id of the route that matched this request.
|
||||
│
|
||||
@@ -383,6 +387,28 @@ then, when handling the request:
|
||||
foobar
|
||||
|
||||
|
||||
``/ssl/client/i/dn`` Resource
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The IP address of the host making the incoming request.
|
||||
|
||||
Sample Usage
|
||||
^^^^^^^^^^^^
|
||||
|
||||
If the user runs:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ curl --cacert path/to/CAfile --cert path/to/clientcredentials http://kapow.example:8080
|
||||
|
||||
using a client certificate with DN=subject@example.net then, when handling the request:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ kapow get /ssl/client/i/dn
|
||||
subject@example.net
|
||||
|
||||
|
||||
``/route/id`` Resource
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -161,6 +161,17 @@ func getRequestFileContent(w http.ResponseWriter, r *http.Request, h *model.Hand
|
||||
}
|
||||
}
|
||||
|
||||
func getSSLClietnDN(w http.ResponseWriter, r *http.Request, h *model.Handler) {
|
||||
if h.Request.TLS == nil {
|
||||
httperror.ErrorJSON(w, ResourceItemNotFound, http.StatusNotFound)
|
||||
} else if h.Request.TLS.VerifiedChains == nil {
|
||||
httperror.ErrorJSON(w, ResourceItemNotFound, http.StatusNotFound)
|
||||
} else {
|
||||
w.Header().Add("Content-Type", "application/octet-stream")
|
||||
_, _ = w.Write([]byte(h.Request.TLS.VerifiedChains[0][0].Subject.CommonName))
|
||||
}
|
||||
}
|
||||
|
||||
func getRouteId(w http.ResponseWriter, r *http.Request, h *model.Handler) {
|
||||
w.Header().Add("Content-Type", "application/octet-stream")
|
||||
_, _ = w.Write([]byte(h.Route.ID))
|
||||
|
||||
@@ -18,7 +18,11 @@ package data
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
@@ -1137,7 +1141,106 @@ func TestGetRequestFileContent500sWhenHandlerRequestErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// DOING #113: /request/ssl/client/i/dn
|
||||
func TestGetSSLClientDNReturns404IfNotHTTPS(t *testing.T) {
|
||||
h := model.Handler{
|
||||
Request: httptest.NewRequest("POST", "/", nil),
|
||||
Writer: httptest.NewRecorder(),
|
||||
}
|
||||
r := httptest.NewRequest("GET", "/not-important-here", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
getSSLClietnDN(w, r, &h)
|
||||
|
||||
if res := w.Result(); res.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("Status code mismatch. Expected: %d, got: %d", http.StatusNotFound, res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSSLClientDNReturns404IfHTTPSButNotmTLS(t *testing.T) {
|
||||
h := model.Handler{
|
||||
Request: httptest.NewRequest("POST", "https://www.foo.bar:8080/", nil),
|
||||
Writer: httptest.NewRecorder(),
|
||||
}
|
||||
r := httptest.NewRequest("GET", "/not-important-here", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
getSSLClietnDN(w, r, &h)
|
||||
|
||||
if res := w.Result(); res.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("Status code mismatch. Expected: %d, got: %d", http.StatusNotFound, res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSSLClientDN200sOnHappyPath(t *testing.T) {
|
||||
h := model.Handler{
|
||||
Request: httptest.NewRequest("POST", "https://www.foo.bar:8080/", nil),
|
||||
Writer: httptest.NewRecorder(),
|
||||
}
|
||||
h.Request.TLS.VerifiedChains = [][]*x509.Certificate{{new(x509.Certificate)}}
|
||||
r := httptest.NewRequest("GET", "/not-important-here", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
getSSLClietnDN(w, r, &h)
|
||||
|
||||
if res := w.Result(); res.StatusCode != http.StatusOK {
|
||||
t.Errorf("Status code mismatch. Expected: %d, got: %d", http.StatusOK, res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSSLClientDNSetsOctectStreamContentType(t *testing.T) {
|
||||
h := model.Handler{
|
||||
Request: httptest.NewRequest("POST", "https://www.foo.bar:8080/", nil),
|
||||
Writer: httptest.NewRecorder(),
|
||||
}
|
||||
h.Request.TLS.VerifiedChains = [][]*x509.Certificate{{new(x509.Certificate)}}
|
||||
r := httptest.NewRequest("GET", "/not-important-here", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
getSSLClietnDN(w, r, &h)
|
||||
|
||||
res := w.Result()
|
||||
if v := res.Header.Get("Content-Type"); v != "application/octet-stream" {
|
||||
t.Errorf("Status code mismatch. Expected: %q, got: %q", "application/octet-stream", v)
|
||||
}
|
||||
}
|
||||
|
||||
func mockAuthenticateClient(tls *tls.ConnectionState) error {
|
||||
fileData, err := ioutil.ReadFile("./testdata/client_chain.crt")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error loading certificates file: %v", err)
|
||||
}
|
||||
|
||||
asn1Data, _ := pem.Decode(fileData)
|
||||
certs, err := x509.ParseCertificates(asn1Data.Bytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing certificates data: %v", err)
|
||||
}
|
||||
|
||||
tls.VerifiedChains = [][]*x509.Certificate{certs}
|
||||
tls.PeerCertificates = []*x509.Certificate{tls.VerifiedChains[0][0]}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestGetSSLClientDNReturnsCorrectDN(t *testing.T) {
|
||||
h := model.Handler{
|
||||
Request: httptest.NewRequest("POST", "https://www.foo.bar:8080/", nil),
|
||||
Writer: httptest.NewRecorder(),
|
||||
}
|
||||
if err := mockAuthenticateClient(h.Request.TLS); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
r := httptest.NewRequest("GET", "/not-important-here", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
getSSLClietnDN(w, r, &h)
|
||||
|
||||
res := w.Result()
|
||||
|
||||
if body, _ := ioutil.ReadAll(res.Body); string(body) != h.Request.TLS.VerifiedChains[0][0].Subject.CommonName {
|
||||
t.Errorf("Body mismatch. Expected: %q, got: %q", h.Request.TLS.VerifiedChains[0][0].Subject.CommonName, string(body))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRouteId200sOnHappyPath(t *testing.T) {
|
||||
h := model.Handler{
|
||||
|
||||
@@ -72,7 +72,10 @@ func Run(bindAddr string, wg *sync.WaitGroup) {
|
||||
{"/handlers/{handlerID}/request/body", "GET", getRequestBody},
|
||||
|
||||
// route
|
||||
//{"/handlers/{handlerID}/route/id", "GET", getRouteId},
|
||||
{"/handlers/{handlerID}/route/id", "GET", getRouteId},
|
||||
|
||||
// SSL stuff
|
||||
{"/handlers/{handlerID}/ssl/client/i/dn", "GET", getSSLClietnDN},
|
||||
|
||||
// response
|
||||
{"/handlers/{handlerID}/response/status", "PUT", lockResponseWriter(setResponseStatus)},
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdTCCAl0CAQIwDQYJKoZIhvcNAQELBQAwgZ8xCzAJBgNVBAYTAkVTMQ8wDQYD
|
||||
VQQIDAZNYWRyaWQxDzANBgNVBAcMBk1hZHJpZDENMAsGA1UECgwEQkJWQTEYMBYG
|
||||
A1UECwwPSW5ub3ZhdGlvbiBMYWJzMR0wGwYDVQQDDBRTZWN1cml0eS1DQS5iYnZh
|
||||
LmNvbTEmMCQGCSqGSIb3DQEJARYXc2VjdXJpdHkuZ3JvdXBAYmJ2YS5jb20wHhcN
|
||||
MjAwMTIzMTQwODUxWhcNMjEwMTIyMTQwODUxWjBhMQswCQYDVQQGEwJFUzEPMA0G
|
||||
A1UECAwGTWFkcmlkMQ0wCwYDVQQKDARCQlZBMRgwFgYDVQQLDA9Jbm5vdmF0aW9u
|
||||
IExhYnMxGDAWBgNVBAMMD0thcG93ISBjbGllbnQgMTCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBAJKXoqOe0S1i8c0bDLGsvibSpDmWkb/2oXn4qn8XtlLF
|
||||
PSY69qeqkeLZov0nVV6zenag9Vh99uy7M4kw/pFqYv1eViDvV6I0wyKEzXNyeQmL
|
||||
O11YUWfP38T+Usw0JY0Pau+ewSQkurtHmGRWC5fAgPxiyi03hD3V3eHPR60V5yE6
|
||||
1wYoLqz6xJ7nkXVyVLdg6wekrMkpos3ciA3Roco4m5fbXbVGYrx8E97byT9yyKzI
|
||||
kvFjQ0T4+67dPuWW+juoD0lOwfNu1WtY4PPnkUuzjUftKzD1t/a4zsLcFLafuuVR
|
||||
f4qb21o2pqDOWxMMZGrceS5uEF0nI1wtdw+XMtFHdFcCAwEAATANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEAsnPW8pCIiejBwjQ4TTNPo5wiRNOib69ANj2lHE1gidO8HA29/ssF
|
||||
U7jbcxCQf0/flv+JddnSJzmeFhrt15CL6nOZ1whSqVA1W1dAno0RYNiPUILofq50
|
||||
zKNUVF+eYz24nksdI87d9j1Zri2H91p+gA1pBnIxBE8zgXZ5+u7FUrA41HOuVyAy
|
||||
55EwUDloVg4WBeddb8Y/mPgXNHS7ZB0Z13+bLHeSkSWWV3Gw1OtLHJEv/j+/9K5O
|
||||
KoJCyO4xSkKP7/nYYKCed4grIfOAu7iHqN/Ok9yuAOm0tbgwKmzw9EGga82YCH0M
|
||||
jfd8wfVCqiZvW9SUa11fM/Np9/+04QtlNA==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEITCCAwmgAwIBAgIUMAqlEXi1gcpy97bWApqtBwgqEvgwDQYJKoZIhvcNAQEL
|
||||
BQAwgZ8xCzAJBgNVBAYTAkVTMQ8wDQYDVQQIDAZNYWRyaWQxDzANBgNVBAcMBk1h
|
||||
ZHJpZDENMAsGA1UECgwEQkJWQTEYMBYGA1UECwwPSW5ub3ZhdGlvbiBMYWJzMR0w
|
||||
GwYDVQQDDBRTZWN1cml0eS1DQS5iYnZhLmNvbTEmMCQGCSqGSIb3DQEJARYXc2Vj
|
||||
dXJpdHkuZ3JvdXBAYmJ2YS5jb20wHhcNMjAwMTIyMTcxNzUwWhcNMjAwMjIxMTcx
|
||||
NzUwWjCBnzELMAkGA1UEBhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwG
|
||||
TWFkcmlkMQ0wCwYDVQQKDARCQlZBMRgwFgYDVQQLDA9Jbm5vdmF0aW9uIExhYnMx
|
||||
HTAbBgNVBAMMFFNlY3VyaXR5LUNBLmJidmEuY29tMSYwJAYJKoZIhvcNAQkBFhdz
|
||||
ZWN1cml0eS5ncm91cEBiYnZhLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||
AQoCggEBAMM8xmTpUg2PXV6zPohGOKLiXHP3nQMfkoYLRwFYcKq0WnYmaKmIK5T0
|
||||
gCLmiQMCNv/NV8+aIkd2HuTPBnobFLbyUCN9yf2Gj81wxeRydBwbjaj0dpB1Jx9W
|
||||
5OdMBFxGIKmnqVN/z784Ma9cj+tv0t5LYpWIxnEgnBaiuMkQnwJFyv6aL1VNRmW7
|
||||
zFMAqOiMishMKb/0UaSW53ZBFjCqmhquZ7CZYLsaB+mMDv1fOXf8jSX/WrcCCkvQ
|
||||
HX04/9HxNVIe3a0Zl8CPfyUOd9njVl1VRDgjljUyMeMM4zo31enpwWOuhpfI6jU5
|
||||
AB7If6xufHyN8FCvKpDy9Z9Sp5Ww1o8CAwEAAaNTMFEwHQYDVR0OBBYEFOfc0yly
|
||||
jUPuoy54Ods9KKt5CITsMB8GA1UdIwQYMBaAFOfc0ylyjUPuoy54Ods9KKt5CITs
|
||||
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBALvzf14HF78rowNA
|
||||
XuczbkgKLbNJam0YC3ZxoB/pxcwfSZCf4E7+FAWKfmwrNqZv2PweOvDfP4Rx4T1x
|
||||
VLWDr0qtcsol7gOPga8HD/1zTgK096rYs5pCxQabgIpmHhzDUnjBrQyds8U4sakP
|
||||
3xzuy/eZ/ozDzCGZn8HzqYHDTcEfypMSdZUUDgN4vVE3Il3AZRSEG9X/Ov4W8tLF
|
||||
dofY/JDVObXV0DAms9xcux8BfAslh8NNytP3q+uneIeuJT/eRQu+Z5GRB+mbL8X6
|
||||
DmXeSSkMiVeFgD1qg9VZVSBsihBpWpJakcxvXOOj5fY2t5ovW6TR2jDjW5XjHs82
|
||||
idlvJUw=
|
||||
-----END CERTIFICATE-----
|
||||
Reference in New Issue
Block a user