feat(auth-app): list and delete endpoints
Signed-off-by: jkoberg <jkoberg@owncloud.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
package defaults
|
package defaults
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
|
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
|
||||||
"github.com/owncloud/ocis/v2/ocis-pkg/structs"
|
"github.com/owncloud/ocis/v2/ocis-pkg/structs"
|
||||||
"github.com/owncloud/ocis/v2/services/auth-app/pkg/config"
|
"github.com/owncloud/ocis/v2/services/auth-app/pkg/config"
|
||||||
@@ -29,8 +31,8 @@ func DefaultConfig() *config.Config {
|
|||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
},
|
},
|
||||||
HTTP: config.HTTP{
|
HTTP: config.HTTP{
|
||||||
Addr: "127.0.0.1:0",
|
Addr: "127.0.0.1:9247",
|
||||||
Namespace: "com.owncloud.api",
|
Namespace: "com.owncloud.web",
|
||||||
Root: "/",
|
Root: "/",
|
||||||
CORS: config.CORS{
|
CORS: config.CORS{
|
||||||
AllowedOrigins: []string{"*"},
|
AllowedOrigins: []string{"*"},
|
||||||
@@ -90,9 +92,16 @@ func EnsureDefaults(cfg *config.Config) {
|
|||||||
if cfg.GRPC.TLS == nil && cfg.Commons != nil {
|
if cfg.GRPC.TLS == nil && cfg.Commons != nil {
|
||||||
cfg.GRPC.TLS = structs.CopyOrZeroValue(cfg.Commons.GRPCServiceTLS)
|
cfg.GRPC.TLS = structs.CopyOrZeroValue(cfg.Commons.GRPCServiceTLS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.Commons != nil {
|
||||||
|
cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sanitize sanitized the configuration
|
// Sanitize sanitized the configuration
|
||||||
func Sanitize(_ *config.Config) {
|
func Sanitize(cfg *config.Config) {
|
||||||
// nothing to sanitize here atm
|
// sanitize config
|
||||||
|
if cfg.HTTP.Root != "/" {
|
||||||
|
cfg.HTTP.Root = strings.TrimSuffix(cfg.HTTP.Root, "/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,31 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
applications "github.com/cs3org/go-cs3apis/cs3/auth/applications/v1beta1"
|
||||||
|
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
|
||||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||||
|
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||||
|
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||||
|
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||||
|
"github.com/cs3org/reva/v2/pkg/auth/scope"
|
||||||
|
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
|
||||||
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||||
|
"github.com/owncloud/ocis/v2/services/auth-app/pkg/config"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthAppService defines the service interface.
|
// AuthAppService defines the service interface.
|
||||||
type AuthAppService struct {
|
type AuthAppService struct {
|
||||||
|
log log.Logger
|
||||||
|
cfg *config.Config
|
||||||
gws pool.Selectable[gateway.GatewayAPIClient]
|
gws pool.Selectable[gateway.GatewayAPIClient]
|
||||||
m *chi.Mux
|
m *chi.Mux
|
||||||
}
|
}
|
||||||
@@ -22,12 +37,16 @@ func NewAuthAppService(opts ...Option) (*AuthAppService, error) {
|
|||||||
opt(o)
|
opt(o)
|
||||||
}
|
}
|
||||||
a := &AuthAppService{
|
a := &AuthAppService{
|
||||||
|
log: o.Logger,
|
||||||
|
cfg: o.Config,
|
||||||
gws: o.GatewaySelector,
|
gws: o.GatewaySelector,
|
||||||
m: o.Mux,
|
m: o.Mux,
|
||||||
}
|
}
|
||||||
|
|
||||||
a.m.Route("/auth-app/tokens", func(r chi.Router) {
|
a.m.Route("/auth-app/tokens", func(r chi.Router) {
|
||||||
|
r.Get("/", a.HandleList)
|
||||||
r.Post("/", a.HandleCreate)
|
r.Post("/", a.HandleCreate)
|
||||||
|
r.Delete("/", a.HandleDelete)
|
||||||
})
|
})
|
||||||
|
|
||||||
return a, nil
|
return a, nil
|
||||||
@@ -38,7 +57,171 @@ func (a *AuthAppService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
a.m.ServeHTTP(w, r)
|
a.m.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleCreate handles the creation of a new auth-token
|
// HandleCreate handles the creation of app tokens
|
||||||
func (a *AuthAppService) HandleCreate(w http.ResponseWriter, r *http.Request) {
|
func (a *AuthAppService) HandleCreate(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Println("ALIVE")
|
gwc, err := a.gws.Next()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "error getting gateway client", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := getContext(r)
|
||||||
|
|
||||||
|
q := r.URL.Query()
|
||||||
|
cid := buildClientID(q.Get("userID"), q.Get("userName"))
|
||||||
|
if cid != "" {
|
||||||
|
ctx, err = a.authenticateUser(cid, gwc)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error().Err(err).Msg("error authenticating user")
|
||||||
|
http.Error(w, "error authenticating user", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scopes, err := scope.AddOwnerScope(map[string]*authpb.Scope{})
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error().Err(err).Msg("error adding owner scope")
|
||||||
|
http.Error(w, "error adding owner scope", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := gwc.GenerateAppPassword(ctx, &applications.GenerateAppPasswordRequest{
|
||||||
|
TokenScope: scopes,
|
||||||
|
Label: "Generated via API",
|
||||||
|
Expiration: &types.Timestamp{
|
||||||
|
Seconds: uint64(time.Now().Add(time.Hour).Unix()),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error().Err(err).Msg("error generating app password")
|
||||||
|
http.Error(w, "error generating app password", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.GetStatus().GetCode() != rpc.Code_CODE_OK {
|
||||||
|
a.log.Error().Str("status", res.GetStatus().GetCode().String()).Msg("error generating app password")
|
||||||
|
http.Error(w, "error generating app password: "+res.GetStatus().GetMessage(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(res.GetAppPassword())
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error().Err(err).Msg("error marshaling app password")
|
||||||
|
http.Error(w, "error marshaling app password", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := w.Write(b); err != nil {
|
||||||
|
a.log.Error().Err(err).Msg("error writing response")
|
||||||
|
http.Error(w, "error writing response", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleList handles listing of app tokens
|
||||||
|
func (a *AuthAppService) HandleList(w http.ResponseWriter, r *http.Request) {
|
||||||
|
gwc, err := a.gws.Next()
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error().Err(err).Msg("error getting gateway client")
|
||||||
|
http.Error(w, "error getting gateway client", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := getContext(r)
|
||||||
|
|
||||||
|
res, err := gwc.ListAppPasswords(ctx, &applications.ListAppPasswordsRequest{})
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error().Err(err).Msg("error listing app passwords")
|
||||||
|
http.Error(w, "error listing app passwords", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.GetStatus().GetCode() != rpc.Code_CODE_OK {
|
||||||
|
a.log.Error().Str("status", res.GetStatus().GetCode().String()).Msg("error listing app passwords")
|
||||||
|
http.Error(w, "error listing app passwords: "+res.GetStatus().GetMessage(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(res.GetAppPasswords())
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error().Err(err).Msg("error marshaling app passwords")
|
||||||
|
http.Error(w, "error marshaling app passwords", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := w.Write(b); err != nil {
|
||||||
|
a.log.Error().Err(err).Msg("error writing response")
|
||||||
|
http.Error(w, "error writing response", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleDelete handles deletion of app tokens
|
||||||
|
func (a *AuthAppService) HandleDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
gwc, err := a.gws.Next()
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error().Err(err).Msg("error getting gateway client")
|
||||||
|
http.Error(w, "error getting gateway client", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := getContext(r)
|
||||||
|
|
||||||
|
pw := r.URL.Query().Get("token")
|
||||||
|
if pw == "" {
|
||||||
|
a.log.Info().Msg("missing token")
|
||||||
|
http.Error(w, "missing token", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := gwc.InvalidateAppPassword(ctx, &applications.InvalidateAppPasswordRequest{Password: pw})
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error().Err(err).Msg("error invalidating app password")
|
||||||
|
http.Error(w, "error invalidating app password", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.GetStatus().GetCode() != rpc.Code_CODE_OK {
|
||||||
|
a.log.Error().Str("status", res.GetStatus().GetCode().String()).Msg("error invalidating app password")
|
||||||
|
http.Error(w, "error invalidating app password: "+res.GetStatus().GetMessage(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthAppService) authenticateUser(clientID string, gwc gateway.GatewayAPIClient) (context.Context, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
authRes, err := gwc.Authenticate(ctx, &gateway.AuthenticateRequest{
|
||||||
|
Type: "machine",
|
||||||
|
ClientId: clientID,
|
||||||
|
ClientSecret: a.cfg.MachineAuthAPIKey,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
|
||||||
|
return nil, errors.New("error authenticating user: " + authRes.GetStatus().GetMessage())
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = ctxpkg.ContextSetUser(ctx, &userpb.User{Id: authRes.GetUser().GetId()})
|
||||||
|
return metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, authRes.GetToken()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getContext(r *http.Request) context.Context {
|
||||||
|
ctx := r.Context()
|
||||||
|
return metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, r.Header.Get("X-Access-Token"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildClientID(userID, userName string) string {
|
||||||
|
switch {
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
case userID != "":
|
||||||
|
return "userid:" + userID
|
||||||
|
case userName != "":
|
||||||
|
return "username:" + userName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ func DefaultPolicies() []config.Policy {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Endpoint: "/auth-app/tokens",
|
Endpoint: "/auth-app/tokens",
|
||||||
Service: "com.owncloud.api.auth-app",
|
Service: "com.owncloud.web.auth-app",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user