-
Notifications
You must be signed in to change notification settings - Fork 329
/
sp.go
154 lines (121 loc) · 3.18 KB
/
sp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package saml
import (
"crypto/rsa"
"crypto/x509"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/crewjam/saml"
"github.com/crewjam/saml/samlsp"
"go.uber.org/zap"
)
const defaultNameIdentifier = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
type (
SamlSPService struct {
log *zap.Logger
Enabled bool
IdpURL url.URL
Host url.URL
IDPUserMeta *IdpIdentityPayload
IDPMeta *saml.EntityDescriptor
sp saml.ServiceProvider
handler *samlsp.Middleware
}
SamlSPArgs struct {
Enabled bool
AcsURL string
MetaURL string
SloURL string
SignRequests bool
SignatureMethod string
Binding string
// user meta from idp
IdentityPayload IdpIdentityPayload
IdpURL url.URL
Host url.URL
Certificate *x509.Certificate
PrivateKey *rsa.PrivateKey
IdpMeta *saml.EntityDescriptor
}
)
// NewSamlSPService loads the certificates and registers the
// already fetched IDP metadata into the SAML middleware
func NewSamlSPService(log *zap.Logger, args SamlSPArgs) (s *SamlSPService, err error) {
metadataURL, _ := url.Parse(args.MetaURL)
acsURL, _ := url.Parse(args.AcsURL)
logoutURL, _ := url.Parse(args.SloURL)
sp := saml.ServiceProvider{
Key: args.PrivateKey,
Certificate: args.Certificate,
IDPMetadata: args.IdpMeta,
MetadataURL: *args.Host.ResolveReference(metadataURL),
AcsURL: *args.Host.ResolveReference(acsURL),
SloURL: *args.Host.ResolveReference(logoutURL),
}
if args.SignRequests {
sp.SignatureMethod = args.SignatureMethod
}
// default to GET
if args.Binding == "" {
args.Binding = saml.HTTPRedirectBinding
}
opts := samlsp.Options{
URL: args.Host,
Key: sp.Key,
Certificate: sp.Certificate,
IDPMetadata: args.IdpMeta,
}
// internal samlsp service
handler, err := samlsp.New(opts)
if err != nil {
err = fmt.Errorf("could not init SAML SP handler: %w", err)
return
}
handler.RequestTracker = samlsp.DefaultRequestTracker(opts, &handler.ServiceProvider)
handler.ServiceProvider = sp
handler.Binding = args.Binding
s = &SamlSPService{
log: log,
Enabled: args.Enabled,
sp: sp,
handler: handler,
IdpURL: args.IdpURL,
Host: args.Host,
IDPUserMeta: &args.IdentityPayload,
}
return
}
func (ssp *SamlSPService) NameIdentifier() string {
return strings.TrimPrefix(defaultNameIdentifier, "urn:oasis:names:tc:SAML:1.1:nameid-format:")
}
// GuessIdentifier tries to guess the necessary (email) key
// for external authentication
func (ssp *SamlSPService) GuessIdentifier(payload map[string][]string) string {
tryValues := []string{
ssp.IDPUserMeta.Identifier,
ssp.NameIdentifier(),
defaultNameIdentifier,
"urn:oasis:names:tc:SAML:attribute:subject-id",
"email",
"mail",
}
for _, v := range tryValues {
if _, ok := payload[v]; ok {
return payload[v][0]
}
}
return ""
}
func (ssp *SamlSPService) Handler() *samlsp.Middleware {
return ssp.handler
}
// ServeHTTP enables us to use the service directly
// in the router
func (ssp *SamlSPService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if ssp.handler == nil {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
ssp.handler.ServeHTTP(w, r)
}