internal/beater/middleware/auth_middleware.go (55 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you 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 middleware import ( "context" "github.com/pkg/errors" "github.com/elastic/apm-server/internal/beater/auth" "github.com/elastic/apm-server/internal/beater/headers" "github.com/elastic/apm-server/internal/beater/request" ) // Authenticator provides an interface for authenticating a client. type Authenticator interface { Authenticate(ctx context.Context, kind, value string) (auth.AuthenticationDetails, auth.Authorizer, error) } // AuthMiddleware returns a Middleware to authenticate clients. // // If required is true, then the middleware will prevent unauthenticated // requests. Otherwise the request.Context's Authentication will be set, // and in the case of unauthenticated requests, the Authentication field // will have the zero value and the context will be populated with an // auth.Authorizer that denies all actions and resources. func AuthMiddleware(authenticator Authenticator, required bool) Middleware { return func(h request.Handler) (request.Handler, error) { return func(c *request.Context) { header := c.Request.Header.Get(headers.Authorization) kind, token := auth.ParseAuthorizationHeader(header) details, authorizer, err := authenticator.Authenticate(c.Request.Context(), kind, token) if err != nil { if errors.Is(err, auth.ErrAuthFailed) { if !required { details = auth.AuthenticationDetails{} authorizer = denyAll{} } else { id := request.IDResponseErrorsUnauthorized status := request.MapResultIDToStatus[id] status.Keyword = err.Error() c.Result.Set(id, status.Code, status.Keyword, nil, nil) c.WriteResult() return } } else { c.Result.SetDefault(request.IDResponseErrorsServiceUnavailable) c.Result.Err = err c.WriteResult() return } } c.Authentication = details c.Request = c.Request.WithContext(auth.ContextWithAuthorizer(c.Request.Context(), authorizer)) h(c) // Processors may indicate that a request is unauthorized by returning auth.ErrUnauthorized. if errors.Is(c.Result.Err, auth.ErrUnauthorized) { switch c.Result.ID { case request.IDUnset, request.IDResponseErrorsInternal: id := request.IDResponseErrorsForbidden status := request.MapResultIDToStatus[id] c.Result.Set(id, status.Code, c.Result.Keyword, c.Result.Body, c.Result.Err) } } }, nil } } type denyAll struct{} func (denyAll) Authorize(context.Context, auth.Action, auth.Resource) error { return auth.ErrUnauthorized }