Merge pull request #8 from owncloud/feature/read-more-ldap-attributes
Read more LDAP attributes to fill the user model
This commit is contained in:
@@ -0,0 +1,203 @@
|
|||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
"github.com/owncloud/ocis-graph/pkg/config"
|
||||||
|
"github.com/owncloud/ocis-pkg/log"
|
||||||
|
msgraph "github.com/yaegashi/msgraph.go/v1.0"
|
||||||
|
ldap "gopkg.in/ldap.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Graph defines implements the business logic for Service.
|
||||||
|
type Graph struct {
|
||||||
|
config *config.Config
|
||||||
|
mux *chi.Mux
|
||||||
|
logger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP implements the Service interface.
|
||||||
|
func (g Graph) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
g.mux.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserCtx middleware is used to load an User object from
|
||||||
|
// the URL parameters passed through as the request. In case
|
||||||
|
// the User could not be found, we stop here and return a 404.
|
||||||
|
func (g Graph) UserCtx(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var user *ldap.Entry
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if userID := chi.URLParam(r, "userID"); userID != "" {
|
||||||
|
user, err = g.ldapGetUser(userID)
|
||||||
|
} else {
|
||||||
|
// TODO: we should not give this error out to users
|
||||||
|
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
render.Status(r, http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Info().Msgf("error reading user: %s", err.Error())
|
||||||
|
render.Status(r, http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.WithValue(r.Context(), userIDKey, user)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMe implements the Service interface.
|
||||||
|
func (g Graph) GetMe(w http.ResponseWriter, r *http.Request) {
|
||||||
|
me := createUserModel(
|
||||||
|
"Alice",
|
||||||
|
"1234-5678-9000-000",
|
||||||
|
)
|
||||||
|
|
||||||
|
resp, err := json.Marshal(me)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Status(r, http.StatusOK)
|
||||||
|
render.JSON(w, r, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsers implements the Service interface.
|
||||||
|
func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
|
con, err := g.initLdap()
|
||||||
|
if err != nil {
|
||||||
|
// TODO: we should not give this error out to users
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := g.ldapSearch(con, "(objectclass=*)")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// TODO: we should not give this error out to users
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var users []*msgraph.User
|
||||||
|
|
||||||
|
for _, user := range result.Entries {
|
||||||
|
users = append(
|
||||||
|
users,
|
||||||
|
createUserModelFromLDAP(
|
||||||
|
user,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Status(r, http.StatusOK)
|
||||||
|
render.JSON(w, r, users)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUser implements the Service interface.
|
||||||
|
func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user := r.Context().Value(userIDKey).(*ldap.Entry)
|
||||||
|
|
||||||
|
render.Status(r, http.StatusOK)
|
||||||
|
render.JSON(w, r, createUserModelFromLDAP(user))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Graph) ldapGetUser(userID string) (*ldap.Entry, error) {
|
||||||
|
conn, err := g.initLdap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
filter := fmt.Sprintf("(entryuuid=%s)", userID)
|
||||||
|
result, err := g.ldapSearch(conn, filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(result.Entries) == 0 {
|
||||||
|
return nil, errors.New("user not found")
|
||||||
|
}
|
||||||
|
return result.Entries[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Graph) initLdap() (*ldap.Conn, error) {
|
||||||
|
g.logger.Info().Msg("Dailing ldap.... ")
|
||||||
|
con, err := ldap.Dial("tcp", "localhost:10389")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := con.Bind("cn=admin,dc=example,dc=org", "admin"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return con, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Graph) ldapSearch(con *ldap.Conn, filter string) (*ldap.SearchResult, error) {
|
||||||
|
search := ldap.NewSearchRequest(
|
||||||
|
"ou=users,dc=example,dc=org",
|
||||||
|
ldap.ScopeWholeSubtree,
|
||||||
|
ldap.NeverDerefAliases,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
filter,
|
||||||
|
[]string{"dn",
|
||||||
|
"uid",
|
||||||
|
"givenname",
|
||||||
|
"mail",
|
||||||
|
"displayname",
|
||||||
|
"entryuuid",
|
||||||
|
"sn",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
return con.Search(search)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createUserModel(displayName string, id string) *msgraph.User {
|
||||||
|
return &msgraph.User{
|
||||||
|
DisplayName: &displayName,
|
||||||
|
GivenName: &displayName,
|
||||||
|
DirectoryObject: msgraph.DirectoryObject{
|
||||||
|
Entity: msgraph.Entity{
|
||||||
|
ID: &id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createUserModelFromLDAP(entry *ldap.Entry) *msgraph.User {
|
||||||
|
displayName := entry.GetAttributeValue("displayname")
|
||||||
|
givenName := entry.GetAttributeValue("givenname")
|
||||||
|
mail := entry.GetAttributeValue("mail")
|
||||||
|
surName := entry.GetAttributeValue("sn")
|
||||||
|
id := entry.GetAttributeValue("entryuuid")
|
||||||
|
return &msgraph.User{
|
||||||
|
DisplayName: &displayName,
|
||||||
|
GivenName: &givenName,
|
||||||
|
Surname: &surName,
|
||||||
|
Mail: &mail,
|
||||||
|
DirectoryObject: msgraph.DirectoryObject{
|
||||||
|
Entity: msgraph.Entity{
|
||||||
|
ID: &id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The key type is unexported to prevent collisions with context keys defined in
|
||||||
|
// other packages.
|
||||||
|
type key int
|
||||||
|
|
||||||
|
const userIDKey key = 0
|
||||||
@@ -24,12 +24,17 @@ func (i instrument) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
i.next.ServeHTTP(w, r)
|
i.next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Me implements the Service interface.
|
// GetMe implements the Service interface.
|
||||||
func (i instrument) Me(w http.ResponseWriter, r *http.Request) {
|
func (i instrument) GetMe(w http.ResponseWriter, r *http.Request) {
|
||||||
i.next.Me(w, r)
|
i.next.GetMe(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Users implements the Service interface.
|
// GetUsers implements the Service interface.
|
||||||
func (i instrument) Users(w http.ResponseWriter, r *http.Request) {
|
func (i instrument) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
i.next.Users(w, r)
|
i.next.GetUsers(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsers implements the Service interface.
|
||||||
|
func (i instrument) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
i.next.GetUser(w, r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,17 @@ func (l logging) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
l.next.ServeHTTP(w, r)
|
l.next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Me implements the Service interface.
|
// GetMe implements the Service interface.
|
||||||
func (l logging) Me(w http.ResponseWriter, r *http.Request) {
|
func (l logging) GetMe(w http.ResponseWriter, r *http.Request) {
|
||||||
l.next.Me(w, r)
|
l.next.GetMe(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Users implements the Service interface.
|
// GetUsers implements the Service interface.
|
||||||
func (l logging) Users(w http.ResponseWriter, r *http.Request) {
|
func (l logging) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
l.next.Users(w, r)
|
l.next.GetUsers(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUser implements the Service interface.
|
||||||
|
func (l logging) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l.next.GetUser(w, r)
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-108
@@ -1,21 +1,17 @@
|
|||||||
package svc
|
package svc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/go-chi/render"
|
|
||||||
"github.com/owncloud/ocis-graph/pkg/config"
|
|
||||||
msgraph "github.com/yaegashi/msgraph.go/v1.0"
|
|
||||||
ldap "gopkg.in/ldap.v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service defines the extension handlers.
|
// Service defines the extension handlers.
|
||||||
type Service interface {
|
type Service interface {
|
||||||
ServeHTTP(http.ResponseWriter, *http.Request)
|
ServeHTTP(http.ResponseWriter, *http.Request)
|
||||||
Me(http.ResponseWriter, *http.Request)
|
GetMe(http.ResponseWriter, *http.Request)
|
||||||
Users(http.ResponseWriter, *http.Request)
|
GetUsers(http.ResponseWriter, *http.Request)
|
||||||
|
GetUser(http.ResponseWriter, *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService returns a service implementation for Service.
|
// NewService returns a service implementation for Service.
|
||||||
@@ -28,109 +24,19 @@ func NewService(opts ...Option) Service {
|
|||||||
svc := Graph{
|
svc := Graph{
|
||||||
config: options.Config,
|
config: options.Config,
|
||||||
mux: m,
|
mux: m,
|
||||||
|
logger: &options.Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
m.HandleFunc("/v1.0/me", svc.Me)
|
m.Route("/v1.0", func(r chi.Router) {
|
||||||
m.HandleFunc("/v1.0/users", svc.Users)
|
r.Get("/me", svc.GetMe)
|
||||||
|
r.Route("/users", func(r chi.Router) {
|
||||||
|
r.Get("/", svc.GetUsers)
|
||||||
|
r.Route("/{userID}", func(r chi.Router) {
|
||||||
|
r.Use(svc.UserCtx)
|
||||||
|
r.Get("/", svc.GetUser)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return svc
|
return svc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graph defines implements the business logic for Service.
|
|
||||||
type Graph struct {
|
|
||||||
config *config.Config
|
|
||||||
mux *chi.Mux
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP implements the Service interface.
|
|
||||||
func (g Graph) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
g.mux.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Me implements the Service interface.
|
|
||||||
func (g Graph) Me(w http.ResponseWriter, r *http.Request) {
|
|
||||||
me := createUserModel(
|
|
||||||
"Alice",
|
|
||||||
"1234-5678-9000-000",
|
|
||||||
)
|
|
||||||
|
|
||||||
resp, err := json.Marshal(me)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
render.Status(r, http.StatusOK)
|
|
||||||
render.JSON(w, r, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Users implements the Service interface.
|
|
||||||
func (g Graph) Users(w http.ResponseWriter, r *http.Request) {
|
|
||||||
con, err := ldap.Dial("tcp", "localhost:10389")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// TODO: we should not give this error out to users
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := con.Bind("cn=admin,dc=example,dc=org", "admin"); err != nil {
|
|
||||||
// TODO: we should not give this error out to users
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
search := ldap.NewSearchRequest(
|
|
||||||
"ou=groups,dc=example,dc=org",
|
|
||||||
ldap.ScopeWholeSubtree,
|
|
||||||
ldap.NeverDerefAliases,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
"(objectclass=*)",
|
|
||||||
[]string{
|
|
||||||
"dn",
|
|
||||||
"uuid",
|
|
||||||
"uid",
|
|
||||||
"givenName",
|
|
||||||
"mail",
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
|
|
||||||
result, err := con.Search(search)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// TODO: we should not give this error out to users
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
users := make([]*msgraph.User, len(result.Entries))
|
|
||||||
|
|
||||||
for _, user := range result.Entries {
|
|
||||||
users = append(
|
|
||||||
users,
|
|
||||||
createUserModel(
|
|
||||||
user.DN,
|
|
||||||
"1234-5678-9000-000",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
render.Status(r, http.StatusOK)
|
|
||||||
render.JSON(w, r, users)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createUserModel(displayName string, id string) *msgraph.User {
|
|
||||||
return &msgraph.User{
|
|
||||||
DisplayName: &displayName,
|
|
||||||
GivenName: &displayName,
|
|
||||||
DirectoryObject: msgraph.DirectoryObject{
|
|
||||||
Entity: msgraph.Entity{
|
|
||||||
ID: &id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -20,12 +20,17 @@ func (t tracing) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
t.next.ServeHTTP(w, r)
|
t.next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Me implements the Service interface.
|
// GetMe implements the Service interface.
|
||||||
func (t tracing) Me(w http.ResponseWriter, r *http.Request) {
|
func (t tracing) GetMe(w http.ResponseWriter, r *http.Request) {
|
||||||
t.next.Me(w, r)
|
t.next.GetMe(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Users implements the Service interface.
|
// GetUsers implements the Service interface.
|
||||||
func (t tracing) Users(w http.ResponseWriter, r *http.Request) {
|
func (t tracing) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
t.next.Users(w, r)
|
t.next.GetUsers(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUser implements the Service interface.
|
||||||
|
func (t tracing) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.next.GetUser(w, r)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user