193 lines
4.5 KiB
Go
193 lines
4.5 KiB
Go
package libpolicy
|
|
|
|
import (
|
|
"log"
|
|
"net"
|
|
"net/netip"
|
|
"strings"
|
|
)
|
|
|
|
func contains(slice []string, val string) bool {
|
|
for _, v := range slice {
|
|
if v == val {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type Effect uint8
|
|
|
|
const (
|
|
Allow Effect = iota
|
|
Deny
|
|
NoMatch
|
|
)
|
|
|
|
func IsIPInCIDR(ipStr, cidrStr string) bool {
|
|
// Parse the IP address
|
|
cidrStr = normalizeCIDR(cidrStr)
|
|
log.Println("Parsing IP:", ipStr, " against CIDR:", cidrStr)
|
|
ip := net.ParseIP(ipStr)
|
|
if ip == nil {
|
|
return false // Invalid IP
|
|
}
|
|
|
|
// Parse the CIDR
|
|
_, ipnet, err := net.ParseCIDR(cidrStr)
|
|
if err != nil {
|
|
return false // Invalid CIDR
|
|
}
|
|
|
|
// net.IPNet.Contains() handles IPv4/IPv6 correctly, including IPv4-mapped IPv6
|
|
return ipnet.Contains(ip)
|
|
}
|
|
|
|
func normalizeCIDR(cidrStr string) string {
|
|
cidrStr = strings.TrimSpace(cidrStr)
|
|
if cidrStr == "" {
|
|
return ""
|
|
}
|
|
|
|
// Already has a mask suffix → use as-is
|
|
if strings.Contains(cidrStr, "/") {
|
|
return cidrStr
|
|
}
|
|
|
|
// Try to parse as IP address to determine version
|
|
addr, err := netip.ParseAddr(cidrStr)
|
|
if err != nil {
|
|
return "" // Invalid
|
|
}
|
|
|
|
if addr.Is4() || addr.Is4In6() {
|
|
return cidrStr + "/32"
|
|
}
|
|
return cidrStr + "/128"
|
|
}
|
|
|
|
func verifyRequest(request *Request, policy *Policy) (Effect, string) {
|
|
//conditionReview = []Condition{}
|
|
var effect Effect
|
|
effect = NoMatch
|
|
statementid := "NoMatch"
|
|
|
|
for _, condition := range policy.Conditions {
|
|
|
|
log.Println("Checking Statement: ", condition.StatementID)
|
|
|
|
// Check the principal
|
|
if !contains(condition.Principal, request.Principal) {
|
|
log.Println("No match on principal")
|
|
log.Println("Checked condition principal:", condition.Principal)
|
|
log.Println("Against: request principal:", request.Principal)
|
|
continue
|
|
}
|
|
|
|
if !contains(condition.Actions, request.Action) {
|
|
log.Println("No match on actions")
|
|
log.Println("Checked condition actions:", condition.Actions)
|
|
log.Println("Against: request action:", request.Action)
|
|
continue
|
|
}
|
|
|
|
// Source Check
|
|
sourceMatch := false
|
|
for _, source := range condition.Source {
|
|
if IsIPInCIDR(request.SourceIPAddress, source) {
|
|
sourceMatch = true
|
|
}
|
|
}
|
|
|
|
if sourceMatch == false {
|
|
log.Println("No match on source")
|
|
log.Println("Checked condition source:", condition.Source)
|
|
log.Println("Against: request source:", request.SourceIPAddress)
|
|
continue
|
|
}
|
|
|
|
if condition.Effect == "allow" {
|
|
effect = Allow
|
|
log.Println("Allowed per ")
|
|
log.Printf("Request is allowed by condition: %s\n", condition.StatementID)
|
|
effect = Allow
|
|
statementid = condition.StatementID
|
|
} else if condition.Effect == "deny" {
|
|
// Immediately deny upon explicit deny
|
|
log.Printf("Request is denied by condition: %s\n", condition.StatementID)
|
|
return Deny, condition.StatementID
|
|
}
|
|
|
|
}
|
|
|
|
return effect, statementid
|
|
}
|
|
|
|
/*
|
|
func loadRequestFile(filename string) (*Request, error) {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open '%s' %w", filename, err)
|
|
}
|
|
defer file.Close()
|
|
|
|
var request Request
|
|
if err := json.NewDecoder(file).Decode(&request); err != nil {
|
|
return nil, fmt.Errorf("failed to parse json from '%s': %w", filename, err)
|
|
}
|
|
|
|
fmt.Printf("Successfully loaded request from %s\n", filename)
|
|
return &request, nil
|
|
}
|
|
|
|
func loadPolicyFile(filename string) (*Policy, error) {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open file '%s': %w", filename, err)
|
|
}
|
|
defer file.Close()
|
|
|
|
var policy Policy
|
|
if err := json.NewDecoder(file).Decode(&policy); err != nil {
|
|
return nil, fmt.Errorf("failed to parse JSON from '%s': %w", filename, err)
|
|
}
|
|
|
|
fmt.Printf("Successfully loaded policy from %s\n", filename)
|
|
return &policy, nil
|
|
}
|
|
*/
|
|
/*
|
|
func main() {
|
|
policy, err := loadPolicyFile("policy.json")
|
|
if err != nil {
|
|
fmt.Println("Error: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("Policy structure loaded successfully:")
|
|
pretty, _ := json.MarshalIndent(policy, "", "\t")
|
|
fmt.Println(string(pretty))
|
|
|
|
request, err := loadRequestFile("request.json")
|
|
if err != nil {
|
|
fmt.Println("Error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("Loaded Request successfully\n")
|
|
pretty, _ = json.MarshalIndent(request, "", "\t")
|
|
fmt.Println(string(pretty))
|
|
|
|
effect, statementid := verifyRequest(request, policy)
|
|
|
|
fmt.Println("")
|
|
if effect == NoMatch {
|
|
fmt.Println("No matching conditions found. Defaulting to Deny.")
|
|
} else if effect == Allow {
|
|
fmt.Println("Request is allowed based on matching conditions.")
|
|
} else {
|
|
fmt.Println("Request is denied based on matching conditions.")
|
|
}
|
|
println("statementid:", statementid)
|
|
|
|
}
|
|
*/
|