2024-10-27 06:10:27 +01:00
package middleware
/ *
Copyright 2024 Jonas Kaninda
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 .
* /
import (
"fmt"
2024-11-04 08:48:38 +01:00
"github.com/jkaninda/goma-gateway/pkg/logger"
2024-10-27 06:10:27 +01:00
"github.com/jkaninda/goma-gateway/util"
"net/http"
"strings"
"time"
)
2024-10-30 16:45:13 +01:00
// AccessMiddleware checks if the request path is forbidden and returns 403 Forbidden
func ( blockList AccessListMiddleware ) AccessMiddleware ( next http . Handler ) http . Handler {
2024-10-27 06:10:27 +01:00
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
for _ , block := range blockList . List {
if isPathBlocked ( r . URL . Path , util . ParseURLPath ( blockList . Path + block ) ) {
2024-11-02 11:55:37 +01:00
logger . Error ( "%s: %s access forbidden" , getRealIP ( r ) , r . URL . Path )
2024-11-14 00:26:21 +01:00
RespondWithError ( w , http . StatusForbidden , fmt . Sprintf ( "%d you do not have permission to access this resource" , http . StatusForbidden ) , blockList . ErrorInterceptor )
2024-10-27 06:10:27 +01:00
return
}
}
next . ServeHTTP ( w , r )
} )
}
// Helper function to determine if the request path is blocked
func isPathBlocked ( requestPath , blockedPath string ) bool {
// Handle exact match
if requestPath == blockedPath {
return true
}
// Handle wildcard match (e.g., /admin/* should block /admin and any subpath)
if strings . HasSuffix ( blockedPath , "/*" ) {
basePath := strings . TrimSuffix ( blockedPath , "/*" )
if strings . HasPrefix ( requestPath , basePath ) {
return true
}
}
return false
}
// NewRateLimiter creates a new rate limiter with the specified refill rate and token capacity
func NewRateLimiter ( maxTokens int , refillRate time . Duration ) * TokenRateLimiter {
return & TokenRateLimiter {
tokens : maxTokens ,
maxTokens : maxTokens ,
refillRate : refillRate ,
lastRefill : time . Now ( ) ,
}
}
// Allow checks if a request is allowed based on the current token bucket
func ( rl * TokenRateLimiter ) Allow ( ) bool {
rl . mu . Lock ( )
defer rl . mu . Unlock ( )
// Refill tokens based on the time elapsed
now := time . Now ( )
elapsed := now . Sub ( rl . lastRefill )
tokensToAdd := int ( elapsed / rl . refillRate )
if tokensToAdd > 0 {
rl . tokens = min ( rl . maxTokens , rl . tokens + tokensToAdd )
rl . lastRefill = now
}
// Check if there are enough tokens to allow the request
if rl . tokens > 0 {
rl . tokens --
return true
}
// Reject request if no tokens are available
return false
}