Repository navigation
Expand file tree
/
Copy pathservice_validate.go
More file actions
160 lines (121 loc) · 4.21 KB
/
Copy pathservice_validate.go
File metadata and controls
160 lines (121 loc) · 4.21 KB
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
155
156
157
158
159
160
package cas
import (
"fmt"
"io"
"log/slog"
"net/http"
"net/url"
"path"
)
// NewServiceTicketValidator create a new *ServiceTicketValidator
func NewServiceTicketValidator(client *http.Client, casURL *url.URL) *ServiceTicketValidator {
return &ServiceTicketValidator{
client: client,
casURL: casURL,
}
}
// ServiceTicketValidator is responsible for the validation of a service ticket
type ServiceTicketValidator struct {
client *http.Client
casURL *url.URL
}
// ValidateTicket validates the service ticket for the given server. The method will try to use the service validate
// endpoint of the cas >= 2 protocol, if the service validate endpoint not available, the function will use the cas 1
// validate endpoint.
func (validator *ServiceTicketValidator) ValidateTicket(serviceURL *url.URL, ticket string) (*AuthenticationResponse, error) {
slog.Info("cas: validating ticket", slog.Any("ticket", ticket), slog.Any("service", serviceURL))
u, err := validator.ServiceValidateUrl(serviceURL, ticket)
if err != nil {
return nil, err
}
r, err := http.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
r.Header.Add("User-Agent", "Golang CAS client gopkg.in/cas")
slog.Info("cas: attempting ticket validation", slog.Any("url", r.URL))
resp, err := validator.client.Do(r)
if err != nil {
return nil, err
}
slog.Info("cas: request returned", slog.Any("method", r.Method), slog.Any("url", r.URL), slog.Any("status", resp.Status))
if resp.StatusCode == http.StatusNotFound {
return validator.validateTicketCas1(serviceURL, ticket)
}
body, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("cas: validate ticket: %v", string(body))
}
slog.Info("cas: received authentication response", slog.Any("response", string(body)))
success, err := ParseServiceResponse(body)
if err != nil {
return nil, err
}
slog.Info("cas: parsed service response", slog.Any("response", success))
return success, nil
}
// ServiceValidateUrl creates the service validation url for the cas >= 2 protocol.
// TODO the function is only exposed, because of the clients ServiceValidateUrl function
func (validator *ServiceTicketValidator) ServiceValidateUrl(serviceURL *url.URL, ticket string) (string, error) {
u, err := validator.casURL.Parse(path.Join(validator.casURL.Path, "serviceValidate"))
if err != nil {
return "", err
}
q := u.Query()
q.Add("service", sanitisedURLString(serviceURL))
q.Add("ticket", ticket)
u.RawQuery = q.Encode()
return u.String(), nil
}
func (validator *ServiceTicketValidator) validateTicketCas1(serviceURL *url.URL, ticket string) (*AuthenticationResponse, error) {
u, err := validator.ValidateUrl(serviceURL, ticket)
if err != nil {
return nil, err
}
r, err := http.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
r.Header.Add("User-Agent", "Golang CAS client gopkg.in/cas")
slog.Info("cas: attempting ticket validation", slog.Any("url", r.URL))
resp, err := validator.client.Do(r)
if err != nil {
return nil, err
}
slog.Info("cas: request returned", slog.Any("method", r.Method), slog.Any("url", r.URL), slog.Any("status", resp.Status))
data, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
body := string(data)
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("cas: validate ticket: %v", body)
}
slog.Info("cas: received authentication response", slog.Any("response", body))
if body == "no\n\n" {
return nil, nil // not logged in
}
success := &AuthenticationResponse{
User: body[4 : len(body)-1],
}
slog.Info("cas: parsed service response", slog.Any("response", success))
return success, nil
}
// ValidateUrl creates the validation url for the cas >= 1 protocol.
// TODO the function is only exposed, because of the clients ValidateUrl function
func (validator *ServiceTicketValidator) ValidateUrl(serviceURL *url.URL, ticket string) (string, error) {
u, err := validator.casURL.Parse(path.Join(validator.casURL.Path, "validate"))
if err != nil {
return "", err
}
q := u.Query()
q.Add("service", sanitisedURLString(serviceURL))
q.Add("ticket", ticket)
u.RawQuery = q.Encode()
return u.String(), nil
}