github.com/crewjam/saml@v0.4.14/example/service.go (about) 1 // This is an example that implements a bitly-esque short link service. 2 package main 3 4 import ( 5 "bytes" 6 "context" 7 "crypto/rsa" 8 "crypto/tls" 9 "crypto/x509" 10 "encoding/xml" 11 "flag" 12 "fmt" 13 "net/http" 14 "net/url" 15 "strings" 16 17 "github.com/dchest/uniuri" 18 "github.com/kr/pretty" 19 "github.com/zenazn/goji" 20 "github.com/zenazn/goji/web" 21 22 "github.com/crewjam/saml/samlsp" 23 ) 24 25 var links = map[string]Link{} 26 27 // Link represents a short link 28 type Link struct { 29 ShortLink string 30 Target string 31 Owner string 32 } 33 34 // CreateLink handles requests to create links 35 func CreateLink(_ web.C, w http.ResponseWriter, r *http.Request) { 36 account := r.Header.Get("X-Remote-User") 37 l := Link{ 38 ShortLink: uniuri.New(), 39 Target: r.FormValue("t"), 40 Owner: account, 41 } 42 links[l.ShortLink] = l 43 44 fmt.Fprintf(w, "%s\n", l.ShortLink) 45 } 46 47 // ServeLink handles requests to redirect to a link 48 func ServeLink(_ web.C, w http.ResponseWriter, r *http.Request) { 49 l, ok := links[strings.TrimPrefix(r.URL.Path, "/")] 50 if !ok { 51 http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) 52 return 53 } 54 http.Redirect(w, r, l.Target, http.StatusFound) 55 } 56 57 // ListLinks returns a list of the current user's links 58 func ListLinks(_ web.C, w http.ResponseWriter, r *http.Request) { 59 account := r.Header.Get("X-Remote-User") 60 for _, l := range links { 61 if l.Owner == account { 62 fmt.Fprintf(w, "%s\n", l.ShortLink) 63 } 64 } 65 } 66 67 var ( 68 key = []byte(`-----BEGIN RSA PRIVATE KEY----- 69 MIICXgIBAAKBgQDU8wdiaFmPfTyRYuFlVPi866WrH/2JubkHzp89bBQopDaLXYxi 70 3PTu3O6Q/KaKxMOFBqrInwqpv/omOGZ4ycQ51O9I+Yc7ybVlW94lTo2gpGf+Y/8E 71 PsVbnZaFutRctJ4dVIp9aQ2TpLiGT0xX1OzBO/JEgq9GzDRf+B+eqSuglwIDAQAB 72 AoGBAMuy1eN6cgFiCOgBsB3gVDdTKpww87Qk5ivjqEt28SmXO13A1KNVPS6oQ8SJ 73 CT5Azc6X/BIAoJCURVL+LHdqebogKljhH/3yIel1kH19vr4E2kTM/tYH+qj8afUS 74 JEmArUzsmmK8ccuNqBcllqdwCZjxL4CHDUmyRudFcHVX9oyhAkEA/OV1OkjM3CLU 75 N3sqELdMmHq5QZCUihBmk3/N5OvGdqAFGBlEeewlepEVxkh7JnaNXAXrKHRVu/f/ 76 fbCQxH+qrwJBANeQERF97b9Sibp9xgolb749UWNlAdqmEpmlvmS202TdcaaT1msU 77 4rRLiQN3X9O9mq4LZMSVethrQAdX1whawpkCQQDk1yGf7xZpMJ8F4U5sN+F4rLyM 78 Rq8Sy8p2OBTwzCUXXK+fYeXjybsUUMr6VMYTRP2fQr/LKJIX+E5ZxvcIyFmDAkEA 79 yfjNVUNVaIbQTzEbRlRvT6MqR+PTCefC072NF9aJWR93JimspGZMR7viY6IM4lrr 80 vBkm0F5yXKaYtoiiDMzlOQJADqmEwXl0D72ZG/2KDg8b4QZEmC9i5gidpQwJXUc6 81 hU+IVQoLxRq0fBib/36K9tcrrO5Ba4iEvDcNY+D8yGbUtA== 82 -----END RSA PRIVATE KEY----- 83 `) 84 cert = []byte(`-----BEGIN CERTIFICATE----- 85 MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJV 86 UzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0 87 MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMx 88 CzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCB 89 nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9 90 ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmH 91 O8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKv 92 Rsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgk 93 akpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeT 94 QLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvn 95 OwJlNCASPZRH/JmF8tX0hoHuAQ== 96 -----END CERTIFICATE----- 97 `) 98 ) 99 100 func main() { 101 rootURLstr := flag.String("url", "https://962766ce.ngrok.io", "The base URL of this service") 102 idpMetadataURLstr := flag.String("idp", "https://516becc2.ngrok.io/metadata", "The metadata URL for the IDP") 103 flag.Parse() 104 105 keyPair, err := tls.X509KeyPair(cert, key) 106 if err != nil { 107 panic(err) // TODO handle error 108 } 109 keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0]) 110 if err != nil { 111 panic(err) // TODO handle error 112 } 113 114 idpMetadataURL, err := url.Parse(*idpMetadataURLstr) 115 if err != nil { 116 panic(err) // TODO handle error 117 } 118 119 idpMetadata, err := samlsp.FetchMetadata(context.Background(), http.DefaultClient, 120 *idpMetadataURL) 121 if err != nil { 122 panic(err) // TODO handle error 123 } 124 125 rootURL, err := url.Parse(*rootURLstr) 126 if err != nil { 127 panic(err) // TODO handle error 128 } 129 130 samlSP, err := samlsp.New(samlsp.Options{ 131 URL: *rootURL, 132 Key: keyPair.PrivateKey.(*rsa.PrivateKey), 133 Certificate: keyPair.Leaf, 134 AllowIDPInitiated: true, 135 IDPMetadata: idpMetadata, 136 }) 137 if err != nil { 138 panic(err) // TODO handle error 139 } 140 141 // register with the service provider 142 spMetadataBuf, _ := xml.MarshalIndent(samlSP.ServiceProvider.Metadata(), "", " ") 143 144 spURL := *idpMetadataURL 145 spURL.Path = "/services/sp" 146 resp, err := http.Post(spURL.String(), "text/xml", bytes.NewReader(spMetadataBuf)) 147 148 if err != nil { 149 panic(err) 150 } 151 152 if err := resp.Body.Close(); err != nil { 153 panic(err) 154 } 155 156 goji.Handle("/saml/*", samlSP) 157 158 authMux := web.New() 159 authMux.Use(samlSP.RequireAccount) 160 authMux.Get("/whoami", func(w http.ResponseWriter, r *http.Request) { 161 if _, err := pretty.Fprintf(w, "%# v", r); err != nil { 162 panic(err) 163 } 164 }) 165 authMux.Post("/", CreateLink) 166 authMux.Get("/", ListLinks) 167 168 goji.Handle("/*", authMux) 169 goji.Get("/:link", ServeLink) 170 171 goji.Serve() 172 }