-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #59 from NorskHelsenett/feature/extend-rorerror
Feature/extend rorerror
- Loading branch information
Showing
7 changed files
with
352 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)} | ||
} |
Oops, something went wrong.