114 lines
3.7 KiB
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)
|
|
}
|