graph: refactor auth and middlewares
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
@@ -8,42 +8,17 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/render"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/cs3org/reva/pkg/token"
|
||||
|
||||
msgraph "github.com/owncloud/open-graph-api-go"
|
||||
)
|
||||
|
||||
func getToken(r *http.Request) string {
|
||||
// 1. check Authorization header
|
||||
hdr := r.Header.Get("Authorization")
|
||||
t := strings.TrimPrefix(hdr, "Bearer ")
|
||||
if t != "" {
|
||||
return t
|
||||
}
|
||||
// TODO 2. check form encoded body parameter for POST requests, see https://tools.ietf.org/html/rfc6750#section-2.2
|
||||
|
||||
// 3. check uri query parameter, see https://tools.ietf.org/html/rfc6750#section-2.3
|
||||
tokens, ok := r.URL.Query()["access_token"]
|
||||
if !ok || len(tokens[0]) < 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return tokens[0]
|
||||
}
|
||||
|
||||
// GetDrives implements the Service interface.
|
||||
func (g Graph) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
g.logger.Info().Msg("Calling GetDrives")
|
||||
if getToken(r) == "" {
|
||||
g.logger.Error().Msg("no access token provided in request")
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
|
||||
client, err := g.GetClient()
|
||||
@@ -52,9 +27,6 @@ func (g Graph) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
t := r.Header.Get("x-access-token")
|
||||
ctx = token.ContextSetToken(ctx, t)
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, "x-access-token", t)
|
||||
|
||||
res, err := client.ListStorageSpaces(ctx, &storageprovider.ListStorageSpacesRequest{})
|
||||
if err != nil {
|
||||
@@ -90,11 +62,6 @@ func (g Graph) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
// GetRootDriveChildren implements the Service interface.
|
||||
func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) {
|
||||
g.logger.Info().Msg("Calling GetRootDriveChildren")
|
||||
if getToken(r) == "" {
|
||||
g.logger.Error().Msg("no access token provided in request")
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
|
||||
fn := g.config.WebdavNamespace
|
||||
@@ -106,12 +73,6 @@ func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
t := r.Header.Get("x-access-token")
|
||||
ctx = token.ContextSetToken(ctx, t)
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, "x-access-token", t)
|
||||
|
||||
g.logger.Info().Interface("context", ctx).Msg("provides access token")
|
||||
|
||||
ref := &storageprovider.Reference{
|
||||
Path: fn,
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ const (
|
||||
ActivityLimitReached
|
||||
// GeneralException defines the error if an unspecified error has occurred.
|
||||
GeneralException
|
||||
// InvalidAuthenticationToken defines the error if the access token is missing
|
||||
InvalidAuthenticationToken
|
||||
// InvalidRange defines the error if the specified byte range is invalid or unavailable.
|
||||
InvalidRange
|
||||
// InvalidRequest defines the error if the request is malformed or incorrect.
|
||||
@@ -47,6 +49,7 @@ var errorCodes = [...]string{
|
||||
"accessDenied",
|
||||
"activityLimitReached",
|
||||
"generalException",
|
||||
"InvalidAuthenticationToken",
|
||||
"invalidRange",
|
||||
"invalidRequest",
|
||||
"itemNotFound",
|
||||
@@ -62,9 +65,10 @@ var errorCodes = [...]string{
|
||||
}
|
||||
|
||||
// Render writes an Graph ErrorObject to the response writer
|
||||
func (e ErrorCode) Render(w http.ResponseWriter, r *http.Request, status int) {
|
||||
func (e ErrorCode) Render(w http.ResponseWriter, r *http.Request, status int, msg string) {
|
||||
resp := &msgraph.ErrorObject{
|
||||
Code: e.String(),
|
||||
Code: e.String(),
|
||||
Message: msg,
|
||||
}
|
||||
render.Status(r, status)
|
||||
render.JSON(w, r, resp)
|
||||
|
||||
@@ -20,7 +20,7 @@ func (g Graph) GroupCtx(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
groupID := chi.URLParam(r, "groupID")
|
||||
if groupID == "" {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest)
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "groupID empty")
|
||||
return
|
||||
}
|
||||
// TODO make filter configurable
|
||||
@@ -28,7 +28,7 @@ func (g Graph) GroupCtx(next http.Handler) http.Handler {
|
||||
group, err := g.ldapGetSingleEntry(g.config.Ldap.BaseDNGroups, filter)
|
||||
if err != nil {
|
||||
g.logger.Info().Err(err).Msgf("Failed to read group %s", groupID)
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound)
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusInternalServerError, "")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func (g Graph) GetGroups(w http.ResponseWriter, r *http.Request) {
|
||||
con, err := g.initLdap()
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("Failed to initialize ldap")
|
||||
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError)
|
||||
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func (g Graph) GetGroups(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("Failed search ldap with filter: '(objectClass=posixGroup)'")
|
||||
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError)
|
||||
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -5,18 +5,19 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis/graph/pkg/service/v0/errorcode"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/cs3org/reva/pkg/user"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/owncloud/ocis/ocis-pkg/oidc"
|
||||
"github.com/owncloud/ocis/graph/pkg/service/v0/errorcode"
|
||||
msgraph "github.com/yaegashi/msgraph.go/v1.0"
|
||||
)
|
||||
|
||||
// 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.
|
||||
// TODO use cs3 api to look up user
|
||||
func (g Graph) UserCtx(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var user *ldap.Entry
|
||||
@@ -24,7 +25,7 @@ func (g Graph) UserCtx(next http.Handler) http.Handler {
|
||||
|
||||
userID := chi.URLParam(r, "userID")
|
||||
if userID == "" {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest)
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "")
|
||||
return
|
||||
}
|
||||
// TODO make filter configurable
|
||||
@@ -32,7 +33,7 @@ func (g Graph) UserCtx(next http.Handler) http.Handler {
|
||||
user, err = g.ldapGetSingleEntry(g.config.Ldap.BaseDNUsers, filter)
|
||||
if err != nil {
|
||||
g.logger.Info().Err(err).Msgf("Failed to read user %s", userID)
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound)
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -43,30 +44,48 @@ func (g Graph) UserCtx(next http.Handler) http.Handler {
|
||||
|
||||
// GetMe implements the Service interface.
|
||||
func (g Graph) GetMe(w http.ResponseWriter, r *http.Request) {
|
||||
claims := oidc.FromContext(r.Context())
|
||||
g.logger.Info().Interface("Claims", claims).Msg("Claims in /me")
|
||||
|
||||
// TODO make filter configurable
|
||||
filter := fmt.Sprintf("(&(objectClass=posixAccount)(cn=%s))", claims.PreferredUsername)
|
||||
user, err := g.ldapGetSingleEntry(g.config.Ldap.BaseDNUsers, filter)
|
||||
if err != nil {
|
||||
g.logger.Info().Err(err).Msgf("Failed to read user %s", claims.PreferredUsername)
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound)
|
||||
u, ok := user.ContextGetUser(r.Context())
|
||||
if !ok {
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "")
|
||||
return
|
||||
}
|
||||
|
||||
me := createUserModelFromLDAP(user)
|
||||
g.logger.Info().Interface("user", u).Msg("User in /me")
|
||||
|
||||
me := createUserModelFromCS3(u)
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, me)
|
||||
}
|
||||
|
||||
func createUserModelFromCS3(u *userpb.User) *msgraph.User {
|
||||
return &msgraph.User{
|
||||
DisplayName: &u.DisplayName,
|
||||
Mail: &u.Mail,
|
||||
// TODO u.Groups
|
||||
OnPremisesSamAccountName: &u.Username,
|
||||
DirectoryObject: msgraph.DirectoryObject{
|
||||
Entity: msgraph.Entity{
|
||||
ID: &u.Id.OpaqueId,
|
||||
Object: msgraph.Object{
|
||||
AdditionalData: map[string]interface{}{
|
||||
"uidnumber": u.UidNumber,
|
||||
"gidnumber": u.GidNumber,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetUsers implements the Service interface.
|
||||
// TODO use cs3 api to look up user
|
||||
func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
con, err := g.initLdap()
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("Failed to initialize ldap")
|
||||
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError)
|
||||
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -75,7 +94,7 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Msg("Failed search ldap with filter: '(objectClass=posixAccount)'")
|
||||
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError)
|
||||
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user