Skip to content

Commit

Permalink
Merge pull request #59 from NorskHelsenett/feature/extend-rorerror
Browse files Browse the repository at this point in the history
Feature/extend rorerror
  • Loading branch information
havardelnan authored Apr 4, 2024
2 parents 2160a02 + 46cefb3 commit 22cd2f8
Show file tree
Hide file tree
Showing 7 changed files with 352 additions and 7 deletions.
3 changes: 3 additions & 0 deletions pkg/apicontracts/apicontracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,9 @@ type Metric struct {
ClusterCount int64 `json:"clusterCount"`
}

// DEPRECATED: User rorerror
//
// github.com/NorskHelsenett/ror/pkg/helpers/rorerror
type Error struct {
Status int `json:"status"`
Message string `json:"message"`
Expand Down
1 change: 0 additions & 1 deletion pkg/auth/userauth/ldaps/ldaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ type LdapConfig struct {
BindUser string `json:"bindUser"`
BindPassword string `json:"bindPassword"`
BaseDN string `json:"basedn"`
Development bool `json:"development,omitempty"`
Servers []LdapServer `json:"servers"`
Certificate []byte `json:"certificate,omitempty"` // This is the CA certificate
}
Expand Down
165 changes: 165 additions & 0 deletions pkg/auth/userauth/openldap/openldap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package openldap

import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"strconv"
"strings"

identitymodels "github.com/NorskHelsenett/ror/pkg/models/identity"
"github.com/NorskHelsenett/ror/pkg/rlog"
"github.com/go-ldap/ldap"
)

type LdapConfig struct {
Domain string `json:"domain"`
BindUser string `json:"bindUser"`
BindPassword string `json:"bindPassword"`
BaseDN string `json:"basedn"`
Servers []LdapServer `json:"servers"`
Certificate []byte `json:"certificate,omitempty"` // This is the CA certificate
}

type LdapServer struct {
Host string `json:"host"`
Port int `json:"port"`
}

type LdapsClient struct {
connection *ldap.Conn
config *LdapConfig
}

func NewLdapsClient(config LdapConfig) (*LdapsClient, error) {
ldapsClient := &LdapsClient{config: &config}

err := ldapsClient.Connect()
if err != nil {
return nil, err
}
return ldapsClient, nil
}
func (l *LdapsClient) Connect() error {
var client *ldap.Conn

for _, ldapserver := range l.config.Servers {
ldapsport, err := strconv.Atoi(ldap.DefaultLdapsPort)
if err != nil {
return fmt.Errorf("failed to parse default ldaps port")
}
if ldapserver.Port == ldapsport {
caCert := l.config.Certificate
caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM(caCert)
if !ok {
return fmt.Errorf("failed to parse root certificate")
}
tlsConf := &tls.Config{
RootCAs: caCertPool,
}

client, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapserver.Host, ldapserver.Port), tlsConf)

} else {
client, err = ldap.DialURL(fmt.Sprintf("ldap://%s:%d", ldapserver.Host, ldapserver.Port))
}

if err != nil {
rlog.Error("an error occurred connecting to LDAP-host.", err, rlog.Any("Host", ldapserver.Host), rlog.Any("Port", ldapserver.Port))
}

err = client.Bind(l.config.BindUser, l.config.BindPassword)
if err != nil {
rlog.Error("an error occurred authenticating to LDAP-host.", err, rlog.Any("Host", ldapserver.Host), rlog.Any("Port", ldapserver.Port), rlog.Any("BindUser", l.config.BindUser))
}
}

if client == nil {
return fmt.Errorf("could not connect to any LDAP server")
}
l.connection = client
return nil
}

func (l *LdapsClient) search(basedn, filter string, attributes []string) (*ldap.SearchResult, error) {
request := ldap.NewSearchRequest(
basedn,
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0,
0,
false,
filter,
attributes,
nil,
)
result, err := l.connection.Search(request)
if err != nil {
return nil, fmt.Errorf("search error: %s", err)
}

if len(result.Entries) > 0 {
return result, nil
}

return nil, fmt.Errorf("could not fetch search entries")
}

func (l *LdapsClient) GetUser(userId string) (*identitymodels.User, error) {

_, domainpart, err := splitUserId(userId)
if err != nil {
return nil, err
}

filter := fmt.Sprintf("(&(objectClass=organizationalPerson)(mail=%s))", userId)

//result, err := l.aearch(client, ldapConfig.BaseDN, filter, []string{"DN", "cn", "mail"})
attributes := []string{"DN", "cn", "mail"}
result, err := l.search(l.config.BaseDN, filter, attributes)

if err != nil {
return nil, err
}
var userEntry *ldap.Entry
if result != nil && len(result.Entries) == 1 {
for _, entry := range result.Entries {
userEntry = entry
}
} else {
return nil, fmt.Errorf("could not find user")
}

groupFilter := fmt.Sprintf("(&(objectClass=groupOfNames)(member=%s))", userEntry.DN)
groups, err := l.search(l.config.BaseDN, groupFilter, []string{"cn", "member"})
if err != nil {
return nil, err
}
userGroups := make([]string, 0)
if groups != nil && len(groups.Entries) > 0 {
for _, entry := range groups.Entries {
userGroups = append(userGroups, fmt.Sprintf("%s@%s", entry.GetAttributeValue("cn"), domainpart))
}
} else {
return nil, errors.New("account has no groups")
}

user := identitymodels.User{
Email: userId,
Name: userEntry.GetAttributeValue("cn"),
IsEmailVerified: true,
Groups: userGroups,
}

return &user, nil
}

func splitUserId(userId string) (string, string, error) {
parts := strings.Split(userId, "@")
if len(parts) != 2 {
return "", "", fmt.Errorf("invalid userId: %s", userId)
}
return parts[1], parts[0], nil
}
20 changes: 16 additions & 4 deletions pkg/auth/userauth/userauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ import (

"github.com/NorskHelsenett/ror/pkg/auth/userauth/ldaps"
"github.com/NorskHelsenett/ror/pkg/auth/userauth/msgraph"
"github.com/NorskHelsenett/ror/pkg/auth/userauth/openldap"
identitymodels "github.com/NorskHelsenett/ror/pkg/models/identity"
)

type DomainResolverConfigs struct {
DomainResolvers []DomainResolverConfig `json:"domainResolvers"`
}
type DomainResolverConfig struct {
ResolverType string `json:"resolverType"`
LdapsConfig *ldaps.LdapConfig `json:"ldapsConfig,omitempty"`
MsGraphConfig *msgraph.MsGraphConfig `json:"msGraphConfig,omitempty"`
ResolverType string `json:"resolverType"`
LdapsConfig *ldaps.LdapConfig `json:"ldapsConfig,omitempty"`
OpenLdapsConfig *openldap.LdapConfig `json:"openLdapConfig,omitempty"`
MsGraphConfig *msgraph.MsGraphConfig `json:"msGraphConfig,omitempty"`
}

type DomainResolverInterface interface {
Expand All @@ -36,6 +38,9 @@ func (d DomainResolvers) GetUser(userId string) (*identitymodels.User, error) {
return nil, fmt.Errorf("no domain resolver found for domain: %s", domain)
}
func (d DomainResolvers) SetDomain(domain string, resolver DomainResolverInterface) {
if resolver == nil {
resolver = &DomainResolvers{}
}
d[domain] = resolver
}

Expand All @@ -53,7 +58,7 @@ func splitUserId(userId string) (string, string, error) {

func NewDomainResolversFromJson(jsonBytes []byte) (*DomainResolvers, error) {
var domainResolverConfigs DomainResolverConfigs
var domainResolvers *DomainResolvers
var domainResolvers *DomainResolvers = &DomainResolvers{}
err := json.Unmarshal(jsonBytes, &domainResolverConfigs)
if err != nil {
return nil, err
Expand All @@ -69,6 +74,13 @@ func NewDomainResolversFromJson(jsonBytes []byte) (*DomainResolvers, error) {
}

switch domainResolverConfig.ResolverType {
case "openldap":
ldapsClient, err := openldap.NewLdapsClient(*domainResolverConfig.OpenLdapsConfig)
if err != nil {
return nil, err
}
domainResolvers.SetDomain(domainResolverConfig.OpenLdapsConfig.Domain, ldapsClient)

case "ldap":
ldapsClient, err := ldaps.NewLdapsClient(*domainResolverConfig.LdapsConfig)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/context/gincontext/gin.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

identitymodels "github.com/NorskHelsenett/ror/pkg/models/identity"

"github.com/NorskHelsenett/ror/pkg/apicontracts"
"github.com/NorskHelsenett/ror/pkg/helpers/rorerror"

"github.com/NorskHelsenett/ror/pkg/rlog"

Expand All @@ -22,7 +22,7 @@ func GetRorContextFromGinContext(c *gin.Context) (context.Context, context.Cance
identity, err := getIdentityFromGinContext(c)
if err != nil {
rlog.Error("could not get user from gin context: %v", err)
c.JSON(http.StatusUnauthorized, apicontracts.Error{
c.JSON(http.StatusUnauthorized, rorerror.RorError{
Status: http.StatusUnauthorized,
Message: "Could not fetch user",
})
Expand Down
89 changes: 89 additions & 0 deletions pkg/helpers/rorerror/fields.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package rorerror

import (
"fmt"

"github.com/NorskHelsenett/ror/pkg/rlog"
)

type Field struct {
Key string
Value string
}

func (f Field) ToRlog() rlog.Field {
return rlog.String(f.Key, f.Value)
}

// Field functions

func String(key, value string) Field {
return Field{Key: key, Value: value}
}

func Int(key string, value int) Field {
return Field{Key: key, Value: fmt.Sprintf("%d", value)}
}
func ByteString(key string, value []byte) Field {
return Field{Key: key, Value: string(value)}
}
func Int64(key string, value int64) Field {
return Field{Key: key, Value: fmt.Sprintf("%d", value)}
}

func Uint(key string, value uint) Field {
return Field{Key: key, Value: fmt.Sprintf("%d", value)}
}

func Float64(key string, value float64) Field {
return Field{Key: key, Value: fmt.Sprintf("%f", value)}
}
func Float32(key string, value float32) Field {
return Field{Key: key, Value: fmt.Sprintf("%f", value)}
}

func Stringp(key string, value *string) Field {
if value == nil {
return Field{Key: key, Value: "nil"}
}
return Field{Key: key, Value: *value}
}

func Intp(key string, value *int) Field {
if value == nil {
return Field{Key: key, Value: "nil"}
}
return Field{Key: key, Value: fmt.Sprintf("%d", *value)}
}
func ByteStringp(key string, value *[]byte) Field {
if value == nil {
return Field{Key: key, Value: "nil"}
}
return Field{Key: key, Value: string(*value)}
}
func Int64p(key string, value *int64) Field {
if value == nil {
return Field{Key: key, Value: "nil"}
}
return Field{Key: key, Value: fmt.Sprintf("%d", *value)}
}

func Uintp(key string, value *uint) Field {
if value == nil {
return Field{Key: key, Value: "nil"}
}
return Field{Key: key, Value: fmt.Sprintf("%d", *value)}
}

func Float64p(key string, value *float64) Field {
if value == nil {
return Field{Key: key, Value: "nil"}
}
return Field{Key: key, Value: fmt.Sprintf("%f", *value)}
}
func Float32p(key string, value *float32) Field {
if value == nil {
return Field{Key: key, Value: "nil"}
}
return Field{Key: key, Value: fmt.Sprintf("%f", *value)}
}
Loading

0 comments on commit 22cd2f8

Please sign in to comment.