forked from jiy1012/cas
-
Notifications
You must be signed in to change notification settings - Fork 1
/
service_response.go
235 lines (200 loc) · 6.45 KB
/
service_response.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
package cas
import (
"encoding/xml"
"fmt"
"reflect"
"strconv"
"strings"
"time"
"gopkg.in/yaml.v2"
)
// AuthenticationError Code values
const (
INVALID_REQUEST = "INVALID_REQUEST"
INVALID_TICKET_SPEC = "INVALID_TICKET_SPEC"
UNAUTHORIZED_SERVICE = "UNAUTHORIZED_SERVICE"
UNAUTHORIZED_SERVICE_PROXY = "UNAUTHORIZED_SERVICE_PROXY"
INVALID_PROXY_CALLBACK = "INVALID_PROXY_CALLBACK"
INVALID_TICKET = "INVALID_TICKET"
INVALID_SERVICE = "INVALID_SERVICE"
INTERNAL_ERROR = "INTERNAL_ERROR"
)
// AuthenticationError represents a CAS AuthenticationFailure response
type AuthenticationError struct {
Code string
Message string
}
// AuthenticationError provides a differentiator for casting.
func (e AuthenticationError) AuthenticationError() bool {
return true
}
// Error returns the AuthenticationError as a string
func (e AuthenticationError) Error() string {
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
// AuthenticationResponse captures authenticated user information
type AuthenticationResponse struct {
User string // Users login name
ProxyGrantingTicket string // Proxy Granting Ticket
Proxies []string // List of proxies
AuthenticationDate time.Time // Time at which authentication was performed
IsNewLogin bool // Whether new authentication was used to grant the service ticket
IsRememberedLogin bool // Whether a long term token was used to grant the service ticket
MemberOf []string // List of groups which the user is a member of
Attributes UserAttributes // Additional information about the user
}
// UserAttributes represents additional data about the user
type UserAttributes map[string][]interface{}
// Get retrieves an attribute by name.
//
// Attributes are stored in arrays. Get will only return the first element.
func (a UserAttributes) Get(name string) interface{} {
if v, ok := a[name]; ok {
return v[0]
}
return ""
}
// Add appends a new attribute.
func (a UserAttributes) Add(name, value string) {
a[name] = append(a[name], value)
}
// ParseServiceResponse returns a successful response or an error
func ParseServiceResponse(data []byte) (*AuthenticationResponse, error) {
var x xmlServiceResponse
if err := xml.Unmarshal(data, &x); err != nil {
return nil, err
}
if x.Failure != nil {
msg := strings.TrimSpace(x.Failure.Message)
err := &AuthenticationError{Code: x.Failure.Code, Message: msg}
return nil, err
}
r := &AuthenticationResponse{
User: x.Success.User,
ProxyGrantingTicket: x.Success.ProxyGrantingTicket,
Attributes: make(UserAttributes),
}
if p := x.Success.Proxies; p != nil {
r.Proxies = p.Proxies
}
if a := x.Success.Attributes; a != nil {
r.AuthenticationDate = a.AuthenticationDate
r.IsRememberedLogin = a.LongTermAuthenticationRequestTokenUsed
r.IsNewLogin = a.IsFromNewLogin
r.MemberOf = a.MemberOf
if a.UserAttributes != nil {
for _, ua := range a.UserAttributes.Attributes {
if ua.Name == "" {
continue
}
r.Attributes.Add(ua.Name, strings.TrimSpace(ua.Value))
}
for _, ea := range a.UserAttributes.AnyAttributes {
r.Attributes.Add(ea.XMLName.Local, strings.TrimSpace(ea.Value))
}
}
if a.ExtraAttributes != nil {
for _, ea := range a.ExtraAttributes {
r.Attributes.Add(ea.XMLName.Local, strings.TrimSpace(ea.Value))
}
}
}
for _, ea := range x.Success.ExtraAttributes {
addRubycasAttribute(r.Attributes, ea.XMLName.Local, strings.TrimSpace(ea.Value))
}
return r, nil
}
// addRubycasAttribute handles RubyCAS style additional attributes.
func addRubycasAttribute(attributes UserAttributes, key, value string) {
if !strings.HasPrefix(value, "---") {
attributes.Add(key, value)
return
}
if value == "--- true" {
attributes.Add(key, "true")
return
}
if value == "--- false" {
attributes.Add(key, "false")
return
}
var decoded interface{}
if err := yaml.Unmarshal([]byte(value), &decoded); err != nil {
attributes.Add(key, err.Error())
return
}
switch reflect.TypeOf(decoded).Kind() {
case reflect.Slice:
s := reflect.ValueOf(decoded)
for i := 0; i < s.Len(); i++ {
e := s.Index(i).Interface()
switch reflect.TypeOf(e).Kind() {
case reflect.String:
attributes.Add(key, e.(string))
}
}
case reflect.String:
s := reflect.ValueOf(decoded).Interface()
attributes.Add(key, s.(string))
default:
}
return
}
type ServiceResponse3 struct {
ServiceResponse ServiceResponse
}
type ServiceResponse struct {
AuthenticationSuccess AuthenticationResponse
}
type UserAttrsStruct struct {
CredentialType []string `json:"credentialType"`
Uid []string `json:"uid"`
AuthenticationMethod []string `json:"authenticationMethod"`
DisplayName []string `json:"displayName"`
SuccessfulAuthenticationHandlers []string `json:"successfulAuthenticationHandlers"`
Name []string `json:"name"`
EmployeeId []string `json:"employeeId"`
Email []string `json:"email"`
}
func (this *UserAttrsStruct) ToUserAttrs() (*UserAttrs, error) {
ret := &UserAttrs{}
var err error
if len(this.CredentialType) > 0 {
ret.CredentialType = this.CredentialType[0]
}
if len(this.Uid) > 0 {
ret.Uid, err = strconv.ParseInt(this.Uid[0], 10, 64)
if err != nil {
return nil, err
}
}
if len(this.AuthenticationMethod) > 0 {
ret.AuthenticationMethod = this.AuthenticationMethod[0]
}
if len(this.DisplayName) > 0 {
ret.DisplayName = this.DisplayName[0]
}
if len(this.SuccessfulAuthenticationHandlers) > 0 {
ret.SuccessfulAuthenticationHandlers = this.SuccessfulAuthenticationHandlers[0]
}
if len(this.Name) > 0 {
ret.Name = this.Name[0]
}
if len(this.EmployeeId) > 0 {
ret.EmployeeId = this.EmployeeId[0]
}
if len(this.Email) > 0 {
ret.Email = this.Email[0]
}
return ret, nil
}
type UserAttrs struct {
CredentialType string `json:"credentialType"`
Uid int64 `json:"uid"`
AuthenticationMethod string `json:"authenticationMethod"`
DisplayName string `json:"displayName"`
SuccessfulAuthenticationHandlers string `json:"successfulAuthenticationHandlers"`
Name string `json:"name"`
EmployeeId string `json:"employeeId"`
Email string `json:"email"`
}