mirror of
https://github.com/documize/community.git
synced 2025-08-09 07:25:23 +02:00
177 lines
4.6 KiB
Go
177 lines
4.6 KiB
Go
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
|
|
}
|