Files
policy-manager/createpolicy.go

114 lines
3.7 KiB
Go

package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"time"
)
type CreatePolicyRequest struct {
PolicyName string `json:"policyname"`
PolicyDocument Policy `json:"policy"`
}
func CreatePolicy(w http.ResponseWriter, r *http.Request) {
// Only allow POST method
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// XXX Temporary Account Handler
accountid := r.Header.Get("Account")
if accountid == "" {
http.Error(w, "Account header is required", http.StatusUnauthorized)
return
}
// Optional: enforce content type
if r.Header.Get("Content-Type") != "application/json" {
http.Error(w, "Content-Type must be application/json", http.StatusUnsupportedMediaType)
return
}
// Read body with size limit (protect against huge requests)
const maxBodyBytes = 1 << 20 // 1 MB
r.Body = http.MaxBytesReader(w, r.Body, maxBodyBytes)
// Decode JSON into our struct
var policydocumentrequest CreatePolicyRequest
decoder := json.NewDecoder(r.Body)
decoder.DisallowUnknownFields() // strict mode - reject unknown fields
if err := decoder.Decode(&policydocumentrequest); err != nil {
switch {
case err == io.EOF:
http.Error(w, "Empty request body", http.StatusBadRequest)
case err.Error() == "http: request body too large":
http.Error(w, "Request body too large (max 1MB)", http.StatusRequestEntityTooLarge)
default:
http.Error(w, fmt.Sprintf("Invalid JSON: %v", err), http.StatusBadRequest)
}
return
}
// Optional: basic validation
if policydocumentrequest.PolicyDocument.Actions == nil || policydocumentrequest.PolicyDocument.Effect == "" || policydocumentrequest.PolicyDocument.Resources == nil {
http.Error(w, "Missing required fields: id and title", http.StatusBadRequest)
return
}
// Process the results
log.Printf("New Policy: Account:%s, PolicyIdentifier=%s, Actions=%q, Effect=%q, Resources=%q, Comment=%q",
accountid, policydocumentrequest.PolicyDocument.PolicyIdentifier, policydocumentrequest.PolicyDocument.Actions,
policydocumentrequest.PolicyDocument.Effect, policydocumentrequest.PolicyDocument.Resources, policydocumentrequest.PolicyDocument.Comment)
// Check if policy with the same name already exists for the account
checkExisting := pool.QueryRow(context.Background(),
"SELECT FROM policies WHERE accountid = $1 AND policyname = $2",
accountid, policydocumentrequest.PolicyName)
err := checkExisting.Scan()
if err == nil {
pri := "pri:iam::" + accountid + ":policy/" + policydocumentrequest.PolicyName
response := map[string]interface{}{
"status": "fail",
"pri": pri,
"message": "Policy " + policydocumentrequest.PolicyName + " already exists: " + pri,
"timestamp": fmt.Sprintf("%d", time.Now().Unix()),
}
json.NewEncoder(w).Encode(response)
http.Error(w, "Policy with the same name already exists for this account", http.StatusConflict)
return
}
_, err = pool.Exec(context.Background(),
"INSERT INTO policies (accountid, policyname, document) VALUES($1, $2, $3) ON CONFLICT DO NOTHING",
accountid, policydocumentrequest.PolicyName, policydocumentrequest.PolicyDocument)
if err != nil {
log.Printf("Error inserting policy: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Success response
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
// arnservice:region:account-id:resource-type:resource-id
pri := "pri:iam::" + accountid + ":policy/" + policydocumentrequest.PolicyName
response := map[string]interface{}{
"status": "success",
"pri": pri,
"message": "Policy created",
"timestamp": fmt.Sprintf("%d", time.Now().Unix()),
}
json.NewEncoder(w).Encode(response)
}