|
package server
import (
"context"
"fmt"
"net/http"
"path/filepath"
"github.com/RedHatInsights/insights-operator-utils/collections"
"github.com/RedHatInsights/insights-results-smart-proxy/auth"
"github.com/RedHatInsights/insights-results-smart-proxy/metrics"
types "github.com/RedHatInsights/insights-results-types"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
)
const (
accountType = "Account Type"
accountNotAuthorized = "Account does not have the required permissions"
)
|
setupAuthMiddleware sets up the authentication and authorization middlewares
for the given router.
|
func ( server * HTTPServer ) setupAuthMiddleware ( router * mux . Router ) {
apiPrefix := server . Config . APIv1Prefix
metricsURL := apiPrefix + MetricsEndpoint
openAPIv1URL := apiPrefix + filepath . Base ( server . Config . APIv1SpecFile )
openAPIv2URL := server . Config . APIv2Prefix + filepath . Base ( server . Config . APIv2SpecFile )
infoV1URL := apiPrefix + InfoEndpoint
infoV2URL := server . Config . APIv2Prefix + InfoEndpoint
|
Define noAuthURLs for use in authentication and authorization middleware
|
noAuthURLs := [ ] string {
metricsURL ,
openAPIv1URL ,
openAPIv2URL ,
infoV1URL ,
infoV2URL ,
metricsURL + "?" ,
openAPIv1URL + "?" ,
openAPIv2URL + "?" ,
}
if server . Config . Auth {
router . Use ( func ( next http . Handler ) http . Handler {
return server . Authentication ( next , noAuthURLs )
} )
}
if server . Config . UseRBAC {
router . Use ( func ( next http . Handler ) http . Handler {
return server . Authorization ( next , noAuthURLs )
} )
}
}
|
Authentication middleware for checking auth rights
|
func ( server * HTTPServer ) Authentication ( next http . Handler , noAuthURLs [ ] string ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
|
For specific URLs it is ok to not use auth. mechanisms at all
this is specific to OpenAPI JSON response and for all OPTION HTTP methods
|
if collections . StringInSlice ( r . RequestURI , noAuthURLs ) || r . Method == "OPTIONS" {
next . ServeHTTP ( w , r )
return
}
|
Try to read auth. header from HTTP request (if provided by client)
|
tk , err := auth . DecodeTokenFromHeader ( w , r , server . Config . AuthType )
if err != nil {
handleServerError ( w , err )
return
}
if tk . Identity . AccountNumber == "" || tk . Identity . AccountNumber == "0" {
log . Info ( ) . Msgf ( "anemic tenant found! org_id %v, user data [%+v]" ,
tk . Identity . OrgID , tk . Identity . User ,
)
}
if tk . Identity . OrgID == 0 {
msg := fmt . Sprintf ( "error retrieving requester org_id from token. account_number [%v], user data [%+v]" ,
tk . Identity . AccountNumber ,
tk . Identity . User ,
)
log . Error ( ) . Msg ( msg )
handleServerError ( w , & auth . AuthenticationError { ErrString : msg } )
return
}
if tk . Identity . User . UserID == "" {
tk . Identity . User . UserID = "0"
}
|
Everything went well, proceed with the request and set the
caller to the user retrieved from the parsed token
|
ctx := context . WithValue ( r . Context ( ) , types . ContextKeyUser , tk . Identity )
r = r . WithContext ( ctx )
next . ServeHTTP ( w , r )
} )
}
|
Authorization middleware for checking permissions
|
func ( server * HTTPServer ) Authorization ( next http . Handler , noAuthURLs [ ] string ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
|
for specific URLs it is ok to not use auth. mechanisms at all
this is specific to OpenAPI JSON response and for all OPTION HTTP methods
|
if collections . StringInSlice ( r . RequestURI , noAuthURLs ) || r . Method == "OPTIONS" {
next . ServeHTTP ( w , r )
return
}
token , err := auth . DecodeTokenFromHeader ( w , r , server . Config . AuthType )
if err != nil {
handleServerError ( w , err )
return
}
metrics . RBACIdentityType . WithLabelValues ( token . Identity . Type ) . Inc ( )
|
For now we will only log authorization and only handle service accounts. This logic should
be for all users, but let's first make sure we won't disturb existing users by only
logging unauthorized service accounts
|
if token . Identity . Type == "ServiceAccount" {
log . Debug ( ) . Str ( "client ID" , token . Identity . ServiceAccount . ClientID ) . Msg ( "Received a request from a service account" )
|
Check permissions for service accounts
|
if ! server . rbacClient . IsAuthorized ( auth . GetAuthTokenHeader ( r ) ) {
log . Warn ( ) . Str ( accountType , token . Identity . Type ) . Msg ( accountNotAuthorized )
metrics . RBACServiceAccountRejected . Inc ( )
if server . rbacClient . IsEnforcing ( ) {
handleServerError ( w , & auth . AuthorizationError { ErrString : accountNotAuthorized } )
return
}
}
} else {
log . Debug ( ) . Str ( accountType , token . Identity . Type ) . Msg ( "RBAC is only used with service accounts for now" )
|
handleServerError(w, &auth.AuthorizationError{ErrString: "unknown identity type"})
We don't use return because RBAC is not mandatory for users yet
return
|
}
|
Access is authorized, proceed with the request
|
next . ServeHTTP ( w , r )
} )
}
|
GetCurrentUserID retrieves current user's id from request
|
func ( server * HTTPServer ) GetCurrentUserID ( request * http . Request ) ( types . UserID , error ) {
identity , err := auth . GetAuthToken ( request )
if err != nil {
return types . UserID ( "" ) , err
}
return identity . User . UserID , nil
}
|
GetCurrentOrgID retrieves the ID of the organization the user belongs to
|
func ( server * HTTPServer ) GetCurrentOrgID ( request * http . Request ) ( types . OrgID , error ) {
identity , err := auth . GetAuthToken ( request )
if err != nil {
return types . OrgID ( 0 ) , err
}
return identity . OrgID , nil
}
|
GetCurrentOrgIDUserIDFromToken retrieves the ID of the organization the user belongs to and
the ID of the specific user
|
func ( server * HTTPServer ) GetCurrentOrgIDUserIDFromToken ( request * http . Request ) (
types . OrgID , types . UserID , error ,
) {
identity , err := auth . GetAuthToken ( request )
if err != nil {
log . Err ( err ) . Msg ( "error retrieving identity from token" )
return types . OrgID ( 0 ) , types . UserID ( "0" ) , err
}
return identity . OrgID , identity . User . UserID , nil
}
func ( server * HTTPServer ) SetRBACClient ( client auth . RBACClient ) {
server . rbacClient = client
}
|