1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-25 08:19:40 +02:00

fix(api): add an authenticated access policy to the websocket endpoint (#1979)

* fix(api): add an authenticated access policy to the websocket endpoint

* refactor(api): centralize EndpointAccess validation

* feat(api): validate id query parameter for the /websocket/exec endpoint
This commit is contained in:
Anthony Lapenna 2018-06-18 11:56:31 +02:00 committed by GitHub
parent f3ce5c25de
commit da5a430b8c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 100 additions and 124 deletions

View file

@ -14,9 +14,19 @@ type (
jwtService portainer.JWTService
userService portainer.UserService
teamMembershipService portainer.TeamMembershipService
endpointGroupService portainer.EndpointGroupService
authDisabled bool
}
// RequestBouncerParams represents the required parameters to create a new RequestBouncer instance.
RequestBouncerParams struct {
JWTService portainer.JWTService
UserService portainer.UserService
TeamMembershipService portainer.TeamMembershipService
EndpointGroupService portainer.EndpointGroupService
AuthDisabled bool
}
// RestrictedRequestContext is a data structure containing information
// used in RestrictedAccess
RestrictedRequestContext struct {
@ -28,12 +38,13 @@ type (
)
// NewRequestBouncer initializes a new RequestBouncer
func NewRequestBouncer(jwtService portainer.JWTService, userService portainer.UserService, teamMembershipService portainer.TeamMembershipService, authDisabled bool) *RequestBouncer {
func NewRequestBouncer(parameters *RequestBouncerParams) *RequestBouncer {
return &RequestBouncer{
jwtService: jwtService,
userService: userService,
teamMembershipService: teamMembershipService,
authDisabled: authDisabled,
jwtService: parameters.JWTService,
userService: parameters.UserService,
teamMembershipService: parameters.TeamMembershipService,
endpointGroupService: parameters.EndpointGroupService,
authDisabled: parameters.AuthDisabled,
}
}
@ -70,6 +81,36 @@ func (bouncer *RequestBouncer) AdministratorAccess(h http.Handler) http.Handler
return h
}
// EndpointAccess retrieves the JWT token from the request context and verifies
// that the user can access the specified endpoint.
// An error is returned when access is denied.
func (bouncer *RequestBouncer) EndpointAccess(r *http.Request, endpoint *portainer.Endpoint) error {
tokenData, err := RetrieveTokenData(r)
if err != nil {
return err
}
if tokenData.Role == portainer.AdministratorRole {
return nil
}
memberships, err := bouncer.teamMembershipService.TeamMembershipsByUserID(tokenData.ID)
if err != nil {
return err
}
group, err := bouncer.endpointGroupService.EndpointGroup(endpoint.GroupID)
if err != nil {
return err
}
if !authorizedEndpointAccess(endpoint, group, tokenData.ID, memberships) {
return portainer.ErrEndpointAccessDenied
}
return nil
}
// mwSecureHeaders provides secure headers middleware for handlers.
func mwSecureHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -120,6 +161,10 @@ func (bouncer *RequestBouncer) mwCheckAuthentication(next http.Handler) http.Han
if !bouncer.authDisabled {
var token string
// Optionally, token might be set via the "token" query parameter.
// For example, in websocket requests
token = r.URL.Query().Get("token")
// Get token from the Authorization header
tokens, ok := r.Header["Authorization"]
if ok && len(tokens) >= 1 {