-
Notifications
You must be signed in to change notification settings - Fork 97
/
Copy pathservice_response.go
177 lines (146 loc) · 4.61 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
package cas
import (
"encoding/xml"
"fmt"
"reflect"
"strings"
"time"
"github.com/golang/glog"
"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][]string
// Get retrieves an attribute by name.
//
// Attributes are stored in arrays. Get will only return the first element.
func (a UserAttributes) Get(name string) string {
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:
if glog.V(2) {
kind := reflect.TypeOf(decoded).Kind()
glog.Warningf("cas: service response: unable to parse %v value: %#v (kind: %v)", key, decoded, kind)
}
}
return
}