Skip to content

Commit

Permalink
fix: check write permission in legacy write path (#19980)
Browse files Browse the repository at this point in the history
  • Loading branch information
danxmoran authored Nov 10, 2020
1 parent 2982701 commit d8e7de9
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
1. [19972](https://github.com/influxdata/influxdb/pull/19972): Remove unused 'influx-command-path' option from upgrade command
1. [19969](https://github.com/influxdata/influxdb/pull/19969): Check if CLI configs file already exists during upgrade
1. [19967](https://github.com/influxdata/influxdb/pull/19967): Add 'log-level' option to upgrade command
1. [19980](https://github.com/influxdata/influxdb/pull/19980): Fix authorization checks in the V1 /write API

## v2.0.0-rc.4 [2020-11-05]

Expand Down
3 changes: 1 addition & 2 deletions cmd/influxd/launcher/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -954,8 +954,7 @@ func (m *Launcher) run(ctx context.Context) (err error) {
}
}

// N.B. the BucketService used by the DBRP service doesn't perform authorization.
dbrpSvc := dbrp.NewAuthorizedService(dbrp.NewService(ctx, ts.BucketService, m.kvStore))
dbrpSvc := dbrp.NewAuthorizedService(dbrp.NewService(ctx, authorizer.NewBucketService(ts.BucketService), m.kvStore))

cm := iqlcontrol.NewControllerMetrics([]string{})
m.reg.MustRegister(cm.PrometheusCollectors()...)
Expand Down
29 changes: 29 additions & 0 deletions http/legacy/write_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package legacy

import (
"context"
"fmt"
"io"
"net/http"

Expand Down Expand Up @@ -128,6 +129,11 @@ func (h *WriteHandler) handleWrite(w http.ResponseWriter, r *http.Request) {
}
span.LogKV("bucket_id", bucket.ID)

if err := checkBucketWritePermissions(auth, bucket.OrgID, bucket.ID); err != nil {
h.HandleHTTPError(ctx, err, sw)
return
}

parsed, err := points.NewParser(req.Precision).Parse(ctx, auth.OrgID, bucket.ID, req.Body)
if err != nil {
h.HandleHTTPError(ctx, err, sw)
Expand Down Expand Up @@ -158,6 +164,29 @@ func (h *WriteHandler) findBucket(ctx context.Context, orgID influxdb.ID, db, rp
return h.BucketService.FindBucketByID(ctx, mapping.BucketID)
}

// checkBucketWritePermissions checks an Authorizer for write permissions to a
// specific Bucket.
func checkBucketWritePermissions(auth influxdb.Authorizer, orgID, bucketID influxdb.ID) error {
p, err := influxdb.NewPermissionAtID(bucketID, influxdb.WriteAction, influxdb.BucketsResourceType, orgID)
if err != nil {
return &influxdb.Error{
Code: influxdb.EInternal,
Op: opWriteHandler,
Msg: fmt.Sprintf("unable to create permission for bucket: %v", err),
Err: err,
}
}
if pset, err := auth.PermissionSet(); err != nil || !pset.Allowed(*p) {
return &influxdb.Error{
Code: influxdb.EForbidden,
Op: opWriteHandler,
Msg: "insufficient permissions for write",
Err: err,
}
}
return nil
}

// findMapping finds a DBRPMappingV2 for the database and retention policy
// combination.
func (h *WriteHandler) findMapping(ctx context.Context, orgID influxdb.ID, db, rp string) (*influxdb.DBRPMappingV2, error) {
Expand Down
94 changes: 79 additions & 15 deletions http/legacy/write_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (

"github.com/golang/mock/gomock"
"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/authorizer"
pcontext "github.com/influxdata/influxdb/v2/context"
"github.com/influxdata/influxdb/v2/dbrp"
"github.com/influxdata/influxdb/v2/http/mocks"
Expand Down Expand Up @@ -82,7 +81,7 @@ func TestWriteHandler_BucketAndMappingExists(t *testing.T) {
recordWriteEvent,
)

perms := newPermissions(influxdb.BucketsResourceType, &orgID, nil)
perms := newPermissions(influxdb.WriteAction, influxdb.BucketsResourceType, &orgID, nil)
auth := newAuthorization(orgID, perms...)
ctx := pcontext.SetAuthorizer(context.Background(), auth)
r := newWriteRequest(ctx, lineProtocolBody)
Expand All @@ -94,7 +93,7 @@ func TestWriteHandler_BucketAndMappingExists(t *testing.T) {
handler := NewWriterHandler(&PointsWriterBackend{
HTTPErrorHandler: DefaultErrorHandler,
Logger: zaptest.NewLogger(t),
BucketService: authorizer.NewBucketService(bucketService),
BucketService: bucketService,
DBRPMappingService: dbrp.NewAuthorizedService(dbrpMappingSvc),
PointsWriter: pointsWriter,
EventRecorder: eventRecorder,
Expand All @@ -105,6 +104,79 @@ func TestWriteHandler_BucketAndMappingExists(t *testing.T) {
assert.Equal(t, "", w.Body.String())
}

func TestWriteHandler_BucketAndMappingExistsNoPermissions(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

var (
// Mocked Services
eventRecorder = mocks.NewMockEventRecorder(ctrl)
dbrpMappingSvc = mocks.NewMockDBRPMappingServiceV2(ctrl)
bucketService = mocks.NewMockBucketService(ctrl)
pointsWriter = mocks.NewMockPointsWriter(ctrl)

// Found Resources
orgID = generator.ID()
bucket = &influxdb.Bucket{
ID: generator.ID(),
OrgID: orgID,
Name: "mydb/autogen",
RetentionPolicyName: "autogen",
RetentionPeriod: 72 * time.Hour,
}
mapping = &influxdb.DBRPMappingV2{
OrganizationID: orgID,
BucketID: bucket.ID,
Database: "mydb",
RetentionPolicy: "autogen",
}

lineProtocolBody = "m,t1=v1 f1=2 100"
)

findAutogenMapping := dbrpMappingSvc.
EXPECT().
FindMany(gomock.Any(), influxdb.DBRPMappingFilterV2{
OrgID: &mapping.OrganizationID,
Database: &mapping.Database,
}).Return([]*influxdb.DBRPMappingV2{mapping}, 1, nil)

findBucketByID := bucketService.
EXPECT().
FindBucketByID(gomock.Any(), bucket.ID).Return(bucket, nil)

recordWriteEvent := eventRecorder.EXPECT().
Record(gomock.Any(), gomock.Any())

gomock.InOrder(
findAutogenMapping,
findBucketByID,
recordWriteEvent,
)

perms := newPermissions(influxdb.ReadAction, influxdb.BucketsResourceType, &orgID, nil)
auth := newAuthorization(orgID, perms...)
ctx := pcontext.SetAuthorizer(context.Background(), auth)
r := newWriteRequest(ctx, lineProtocolBody)
params := r.URL.Query()
params.Set("db", "mydb")
params.Set("rp", "")
r.URL.RawQuery = params.Encode()

handler := NewWriterHandler(&PointsWriterBackend{
HTTPErrorHandler: DefaultErrorHandler,
Logger: zaptest.NewLogger(t),
BucketService: bucketService,
DBRPMappingService: dbrp.NewAuthorizedService(dbrpMappingSvc),
PointsWriter: pointsWriter,
EventRecorder: eventRecorder,
})
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
assert.Equal(t, http.StatusForbidden, w.Code)
assert.Equal(t, "{\"code\":\"forbidden\",\"message\":\"insufficient permissions for write\"}", w.Body.String())
}

func TestWriteHandler_MappingNotExists(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
Expand Down Expand Up @@ -150,7 +222,7 @@ func TestWriteHandler_MappingNotExists(t *testing.T) {
recordWriteEvent,
)

perms := newPermissions(influxdb.BucketsResourceType, &orgID, nil)
perms := newPermissions(influxdb.WriteAction, influxdb.BucketsResourceType, &orgID, nil)
auth := newAuthorization(orgID, perms...)
ctx := pcontext.SetAuthorizer(context.Background(), auth)
r := newWriteRequest(ctx, lineProtocolBody)
Expand All @@ -162,7 +234,7 @@ func TestWriteHandler_MappingNotExists(t *testing.T) {
handler := NewWriterHandler(&PointsWriterBackend{
HTTPErrorHandler: DefaultErrorHandler,
Logger: zaptest.NewLogger(t),
BucketService: authorizer.NewBucketService(bucketService),
BucketService: bucketService,
DBRPMappingService: dbrp.NewAuthorizedService(dbrpMappingSvc),
PointsWriter: pointsWriter,
EventRecorder: eventRecorder,
Expand Down Expand Up @@ -230,18 +302,10 @@ func (m pointsMatcher) String() string {
return fmt.Sprintf("%#v", m.points)
}

func newPermissions(resourceType influxdb.ResourceType, orgID, id *influxdb.ID) []influxdb.Permission {
func newPermissions(action influxdb.Action, resourceType influxdb.ResourceType, orgID, id *influxdb.ID) []influxdb.Permission {
return []influxdb.Permission{
{
Action: influxdb.WriteAction,
Resource: influxdb.Resource{
Type: resourceType,
OrgID: orgID,
ID: id,
},
},
{
Action: influxdb.ReadAction,
Action: action,
Resource: influxdb.Resource{
Type: resourceType,
OrgID: orgID,
Expand Down

0 comments on commit d8e7de9

Please sign in to comment.