satellite/admin/back-office: Add method for auth middleware

Add a new method to the Authorizer to use it with the API midleware that
we are going to implement for injecting it into the handler generated by
the API generator.

This new method will reduce the lines of code to generate and avoiding
errors that are more difficult to test in generated code.

The commit deletes the Middleware method because we won't used due to
the API generator doesn't support "standard" middlewares and allows
their customization via code generated and injected inside the handler
base logic generated by the API generator.

Change-Id: Ie427eb2eea94797913e2c357cf097ecf1e2e63ef
This commit is contained in:
Ivan Fraixedes 2023-11-20 16:40:15 +01:00 committed by Storj Robot
parent a3a7df91e3
commit ea022ede46
2 changed files with 52 additions and 52 deletions

View File

@ -144,26 +144,28 @@ func (auth *Authorizer) HasPermissions(group string, perms ...Permission) bool {
return groupAuth.Has(perms...) return groupAuth.Has(perms...)
} }
// Middleware returns an HTTP handler which verifies if the request is performed by a user with a // IsRejected verifies that r is from a user who belongs to a group that has all perms and returns
// role that allows all the passed permissions. // false, otherwise responds with http.StatusUnauthorized using
func (auth *Authorizer) Middleware(next http.Handler, perms ...Permission) http.Handler { // storj.io/storj/private.api.ServeError and returns true.
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { //
// This method is convenient to inject it to the handlers generated by the API generator through a
// customized handler.
func (auth *Authorizer) IsRejected(w http.ResponseWriter, r *http.Request, perms ...Permission) bool {
groupsh := r.Header.Get("X-Forwarded-Groups") groupsh := r.Header.Get("X-Forwarded-Groups")
if groupsh == "" { if groupsh == "" {
err := Error.Wrap(ErrAuthorizer.New("You do not belong to any group")) err := Error.Wrap(ErrAuthorizer.New("You do not belong to any group"))
api.ServeError(auth.log, w, http.StatusUnauthorized, err) api.ServeError(auth.log, w, http.StatusUnauthorized, err)
return return true
} }
groups := strings.Split(groupsh, ",") groups := strings.Split(groupsh, ",")
for _, g := range groups { for _, g := range groups {
if auth.HasPermissions(g, perms...) { if auth.HasPermissions(g, perms...) {
next.ServeHTTP(w, r) return false
return
} }
} }
err := Error.Wrap(ErrAuthorizer.New("Not enough permissions (your groups: %s)", groupsh)) err := Error.Wrap(ErrAuthorizer.New("Not enough permissions (your groups: %s)", groupsh))
api.ServeError(auth.log, w, http.StatusUnauthorized, err) api.ServeError(auth.log, w, http.StatusUnauthorized, err)
}) return true
} }

View File

@ -155,7 +155,7 @@ func TestAuthorizer(t *testing.T) {
require.Equal(t, c.hasAccess, auth.HasPermissions(c.group, c.permissions...)) require.Equal(t, c.hasAccess, auth.HasPermissions(c.group, c.permissions...))
}) })
t.Run("Middleware", func(t *testing.T) { t.Run("isRejected", func(t *testing.T) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://example.test", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://example.test", nil)
require.NoError(t, err) require.NoError(t, err)
req.Header.Add("X-Forwarded-Groups", c.group) req.Header.Add("X-Forwarded-Groups", c.group)
@ -163,19 +163,17 @@ func TestAuthorizer(t *testing.T) {
wbuff := &bytes.Buffer{} wbuff := &bytes.Buffer{}
w.Body = wbuff w.Body = wbuff
nextCalled := false handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { if auth.IsRejected(w, r, c.permissions...) {
nextCalled = true return
}
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
handler := auth.Middleware(next, c.permissions...)
handler.ServeHTTP(w, req) handler.ServeHTTP(w, req)
if c.hasAccess { if c.hasAccess {
assert.True(t, nextCalled, "Next handler is called")
assert.Equal(t, http.StatusOK, w.Code, "HTTP Status Code") assert.Equal(t, http.StatusOK, w.Code, "HTTP Status Code")
} else { } else {
assert.False(t, nextCalled, "Next handler is called")
assert.Equal(t, http.StatusUnauthorized, w.Code, "HTTP Status Code") assert.Equal(t, http.StatusUnauthorized, w.Code, "HTTP Status Code")
assert.Contains(t, wbuff.String(), fmt.Sprintf(`Not enough permissions (your groups: %s)`, c.group)) assert.Contains(t, wbuff.String(), fmt.Sprintf(`Not enough permissions (your groups: %s)`, c.group))
} }
@ -183,7 +181,7 @@ func TestAuthorizer(t *testing.T) {
}) })
} }
t.Run("Middleware request with multiple groups one has the permissions", func(t *testing.T) { t.Run("IsRejected request with multiple groups one has the permissions", func(t *testing.T) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://example.test", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://example.test", nil)
require.NoError(t, err) require.NoError(t, err)
req.Header.Add("X-Forwarded-Groups", "everyone-else,super") req.Header.Add("X-Forwarded-Groups", "everyone-else,super")
@ -191,18 +189,18 @@ func TestAuthorizer(t *testing.T) {
wbuff := &bytes.Buffer{} wbuff := &bytes.Buffer{}
w.Body = wbuff w.Body = wbuff
nextCalled := false handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { if auth.IsRejected(w, r, admin.PermAccountDeleteWithData) {
nextCalled = true return
}
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
handler := auth.Middleware(next, admin.PermAccountDeleteWithData)
handler.ServeHTTP(w, req) handler.ServeHTTP(w, req)
assert.True(t, nextCalled, "Next handler is called")
assert.Equal(t, http.StatusOK, w.Code, "HTTP Status Code") assert.Equal(t, http.StatusOK, w.Code, "HTTP Status Code")
}) })
t.Run("Middleware request with multiple groups none has all the permissions", func(t *testing.T) { t.Run("IsRejected request with multiple groups none has all the permissions", func(t *testing.T) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://example.test", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://example.test", nil)
require.NoError(t, err) require.NoError(t, err)
req.Header.Add("X-Forwarded-Groups", "customers-troubleshooter,everyone-else") req.Header.Add("X-Forwarded-Groups", "customers-troubleshooter,everyone-else")
@ -210,19 +208,19 @@ func TestAuthorizer(t *testing.T) {
wbuff := &bytes.Buffer{} wbuff := &bytes.Buffer{}
w.Body = wbuff w.Body = wbuff
nextCalled := false handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { if auth.IsRejected(w, r, admin.PermAccountDeleteWithData) {
nextCalled = true return
}
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
handler := auth.Middleware(next, admin.PermAccountDeleteWithData)
handler.ServeHTTP(w, req) handler.ServeHTTP(w, req)
assert.False(t, nextCalled, "Next handler is called")
assert.Equal(t, http.StatusUnauthorized, w.Code, "HTTP Status Code") assert.Equal(t, http.StatusUnauthorized, w.Code, "HTTP Status Code")
assert.Contains(t, wbuff.String(), `Not enough permissions (your groups: customers-troubleshooter,everyone-else)`) assert.Contains(t, wbuff.String(), `Not enough permissions (your groups: customers-troubleshooter,everyone-else)`)
}) })
t.Run("Middleware request with a unauthorized group", func(t *testing.T) { t.Run("IsRejected request with a unauthorized group", func(t *testing.T) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://example.test", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://example.test", nil)
require.NoError(t, err) require.NoError(t, err)
req.Header.Add("X-Forwarded-Groups", "engineering") req.Header.Add("X-Forwarded-Groups", "engineering")
@ -230,33 +228,33 @@ func TestAuthorizer(t *testing.T) {
wbuff := &bytes.Buffer{} wbuff := &bytes.Buffer{}
w.Body = wbuff w.Body = wbuff
nextCalled := false handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { if auth.IsRejected(w, r, admin.PermAccountDeleteWithData) {
nextCalled = true return
}
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
handler := auth.Middleware(next, admin.PermAccountDeleteWithData)
handler.ServeHTTP(w, req) handler.ServeHTTP(w, req)
assert.False(t, nextCalled, "Next handler is called")
assert.Equal(t, http.StatusUnauthorized, w.Code, "HTTP Status Code") assert.Equal(t, http.StatusUnauthorized, w.Code, "HTTP Status Code")
assert.Contains(t, wbuff.String(), `Not enough permissions (your groups: engineering)`) assert.Contains(t, wbuff.String(), `Not enough permissions (your groups: engineering)`)
}) })
t.Run("Middleware request with no groups", func(t *testing.T) { t.Run("IsRejected request with no groups", func(t *testing.T) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://example.test", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://example.test", nil)
require.NoError(t, err) require.NoError(t, err)
w := httptest.NewRecorder() w := httptest.NewRecorder()
wbuff := &bytes.Buffer{} wbuff := &bytes.Buffer{}
w.Body = wbuff w.Body = wbuff
nextCalled := false handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { if auth.IsRejected(w, r, admin.PermAccountDeleteWithData) {
nextCalled = true return
}
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
handler := auth.Middleware(next, admin.PermAccountDeleteWithData)
handler.ServeHTTP(w, req) handler.ServeHTTP(w, req)
assert.False(t, nextCalled, "Next handler is called")
assert.Equal(t, http.StatusUnauthorized, w.Code, "HTTP Status Code") assert.Equal(t, http.StatusUnauthorized, w.Code, "HTTP Status Code")
assert.Contains(t, wbuff.String(), "You do not belong to any group") assert.Contains(t, wbuff.String(), "You do not belong to any group")
}) })