harness-drone/handler/api/acl/check.go
2019-07-29 13:58:32 -07:00

146 lines
4.5 KiB
Go

// Copyright 2019 Drone IO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package acl
import (
"net/http"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/errors"
"github.com/drone/drone/handler/api/render"
"github.com/drone/drone/handler/api/request"
"github.com/drone/drone/logger"
"github.com/go-chi/chi"
"github.com/sirupsen/logrus"
)
// CheckReadAccess returns an http.Handler middleware that authorizes only
// authenticated users with read repository access to proceed to the next
// handler in the chain.
func CheckReadAccess() func(http.Handler) http.Handler {
return CheckAccess(true, false, false)
}
// CheckWriteAccess returns an http.Handler middleware that authorizes only
// authenticated users with write repository access to proceed to the next
// handler in the chain.
func CheckWriteAccess() func(http.Handler) http.Handler {
return CheckAccess(true, true, false)
}
// CheckAdminAccess returns an http.Handler middleware that authorizes only
// authenticated users with admin repository access to proceed to the next
// handler in the chain.
func CheckAdminAccess() func(http.Handler) http.Handler {
return CheckAccess(true, true, true)
}
// CheckAccess returns an http.Handler middleware that authorizes only
// authenticated users with the required read, write or admin access
// permissions to the requested repository resource.
func CheckAccess(read, write, admin bool) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
owner = chi.URLParam(r, "owner")
name = chi.URLParam(r, "name")
)
log := logger.FromRequest(r).
WithField("namespace", owner).
WithField("name", name)
user, ok := request.UserFrom(ctx)
switch {
case ok == false && write == true:
render.Unauthorized(w, errors.ErrUnauthorized)
log.Debugln("api: authentication required for write access")
return
case ok == false && admin == true:
render.Unauthorized(w, errors.ErrUnauthorized)
log.Debugln("api: authentication required for admin access")
return
case ok == true && user.Admin == true:
log.Debugln("api: root access granted")
next.ServeHTTP(w, r)
return
}
repo, noRepo := request.RepoFrom(ctx)
if !noRepo {
// this should never happen. the repository
// should always be injected into the context
// by an upstream handler in the chain.
log.Errorln("api: null repository in context")
render.NotFound(w, errors.ErrNotFound)
return
}
log = log.WithField("visibility", repo.Visibility)
switch {
case admin == true: // continue
case write == true: // continue
case repo.Visibility == core.VisibilityPublic:
log.Debugln("api: read access granted")
next.ServeHTTP(w, r)
return
case ok == false:
render.Unauthorized(w, errors.ErrUnauthorized)
log.Debugln("api: authentication required")
return
case ok == true && repo.Visibility == core.VisibilityInternal:
log.Debugln("api: read access granted")
next.ServeHTTP(w, r)
return
}
perm, ok := request.PermFrom(ctx)
if !ok {
render.NotFound(w, errors.ErrNotFound)
log.Debugln("api: repository permissions not found")
return
}
log = log.WithFields(
logrus.Fields{
"read": perm.Read,
"write": perm.Write,
"admin": perm.Admin,
},
)
switch {
case user.Active == false:
render.Forbidden(w, errors.ErrForbidden)
log.Debugln("api: active account required")
case read == true && perm.Read == false:
render.NotFound(w, errors.ErrNotFound)
log.Debugln("api: read access required")
case write == true && perm.Write == false:
render.NotFound(w, errors.ErrNotFound)
log.Debugln("api: write access required")
case admin == true && perm.Admin == false:
render.NotFound(w, errors.ErrNotFound)
log.Debugln("api: admin access required")
default:
log.Debug("api: access granted")
next.ServeHTTP(w, r.WithContext(
request.WithPerm(ctx, perm),
))
}
})
}
}