package rbac import ( "errors" "fmt" "github.com/go-ldap/ldap" "github.com/julienschmidt/httprouter" "github.rackspace.com/gorax/clients/activeDirectory" "github.rackspace.com/gorax/clients/eDir" "github.rackspace.com/gorax/clients/passwordsafe" raxidentity "github.rackspace.com/gorax/clients/raxIdentity" "github.rackspace.com/gorax/tools/passkey" ) type ( // PrivilegeLevel interface exposes the getLevel method that will pull the privilege level for // the user based on the authentication type PrivilegeLevel interface { getLevel() (int, error) } // Identity struct is an authentication type that is used to interface with the // RBAC roles methods Identity struct { Client *raxidentity.Client } // EDir struct is an authentication type that is used to interface with the // RBAC roles methods EDir struct { User string RedisKey string Bind eDir.Bind } // AD struct is an authentication type that is used to interface with the // RBAC roles methods AD struct { User string RedisKey string conn *ldap.Conn } // Route type exposes a route handler that attaches roles to an existing httprouter handler // with a list of acceptable paths to bind to the role and handle. Route struct { Methods map[string]Handler Paths []string } // Handler type exposes the route handler that attaches roles to an existing httprouter handler Handler struct { Handler httprouter.Handle Role int } ) // BindPK function will create the eDir type for use within the RBAC package func BindPK(pk *passwordsafe.Credential) eDir.Bind { return eDir.Bind{ User: pk.Credential.Username, Pass: pk.Credential.Password, } } // BindAD function will create the AD type for use with the RBAC package func BindAD(user string, pk *passkey.Client) (AD, error) { conn, err := activeDirectory.Bind(activeDirectory.BindCreds{ User: pk.Service.Netsec01.Credential.Username, Pass: pk.Service.Netsec01.Credential.Password, }) if err != nil { return AD{}, err } return AD{User: user, RedisKey: pk.Keys.Redis.Credential.Password, conn: conn}, nil } // Allowed function will take a PrivilegeLevel interface and an int representing the RBAC role and perform a // lookup to see if the PrivilegeLevel interface is granted access to the resource. func Allowed(l PrivilegeLevel, roleRequested int) (string, bool) { if roleRequested <= BasicRead { return fmt.Sprintf("Allowed with privilege level %v", roleRequested), true } // Get the RBAC level of the user as authenticated via coreAPI userLevel, _ := GetPrivilegeLevel(l) // If the users level is not greater than the role assigned to the call, deny access. if userLevel < roleRequested { return fmt.Sprintf("Denied with privilege level %v", userLevel), false } return fmt.Sprintf("Allowed with privilege level %v", userLevel), true } func getIdentityLevel(r *raxidentity.Client) int { for _, role := range apiRoles { // Check the explictUsers list for the role, if we find a // match, exit out as the explictUser should override groups. for _, explUser := range role.explicitUsers { if explUser == r.Access.User.Name { return role.lvl } } } // If an explictUser is not found, set the level to BasicRead and iterate // through the roles/apiRoles to look for the highest match lvl := BasicRead for _, u := range r.Access.User.Roles { for _, role := range apiRoles { if role.groupName == u.Name && lvl < role.lvl { lvl = role.lvl } } } // If a higher match is not found, BasicRead is returned return lvl } func getEdirLevel(u *eDir.UserGroup) int { for _, role := range apiRoles { // Check the explictUsers list for the role, if we find a // match, exit out as the explictUser should override groups. for _, explUser := range role.explicitUsers { if explUser == u.SSO { return role.lvl } } } // If an explictUser is not found, set the level to BasicRead and iterate // through the roles/apiRoles to look for the highest match lvl := BasicRead for _, uGroup := range u.Groups { for _, role := range apiRoles { if role.groupName == uGroup && lvl < role.lvl { lvl = role.lvl } } } // If a higher match is not found, BasicRead is returned return lvl } func gatherFromEdir(user string, b eDir.Bind) (ug *eDir.UserGroup, err error) { ul, err := eDir.GetTLSeDirGroups(user, b) if err != nil { return } for _, ui := range ul { if ui.SSO == user { ug = &ui } } return } func gatherFromAD(user string, conn *ldap.Conn) (ug activeDirectory.UserGroup, err error) { defer conn.Close() ul, err := activeDirectory.GetSimpleGroupMemberships(conn, user) if err != nil { return } if ul.SSO == user { ug = ul } return } func getADLevel(u activeDirectory.UserGroup) int { for _, role := range apiRoles { // Check the explictUsers list for the role, if we find a // match, exit out as the explictUser should override groups. for _, explUser := range role.explicitUsers { if explUser == u.SSO { return role.lvl } } } // If an explictUser is not found, set the level to BasicRead and iterate // through the roles/apiRoles to look for the highest match lvl := BasicRead for _, uGroup := range u.Groups { for _, role := range apiRoles { if role.groupName == uGroup && lvl < role.lvl { lvl = role.lvl } } } // If a higher match is not found, BasicRead is returned return lvl } // ParseLevel : Returns a string representation of the given level func ParseLevel(level int) (string, error) { switch level { case NoAccess: return "NoAccess", nil case SuperUser: return "SuperUser", nil case AdvancedWrite: return "AdvancedWrite", nil case BasicWrite: return "BasicWrite", nil case AdvancedRead: return "AdvancedRead", nil case BasicRead: return "BasicRead", nil default: return "", errors.New("unknown level") } }