Templating creating new policy
This commit is contained in:
36
Dockerfile
36
Dockerfile
@@ -1,31 +1,37 @@
|
|||||||
# ---- Build Stage ----
|
# ---- Build Stage ----
|
||||||
FROM golang:latest AS builder
|
FROM golang:1.26 AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy go mod files first (better caching)
|
# ---- Step 1: Copy go.mod/go.sum for caching ----
|
||||||
COPY go.mod ./
|
# Copy only the minimal files first for layer caching
|
||||||
RUN go mod download
|
COPY libshared/go.mod libshared/go.sum ./libshared/
|
||||||
|
COPY policy-manager/go.mod policy-manager/go.sum ./policy-manager/
|
||||||
|
|
||||||
# Copy source code
|
# ---- Step 2: Download dependencies ----
|
||||||
COPY . .
|
RUN go -C libshared mod download
|
||||||
|
RUN go -C policy-manager mod download
|
||||||
|
|
||||||
# Build static binary
|
# ---- Step 3: Copy full source code ----
|
||||||
|
COPY libshared ./libshared
|
||||||
|
COPY policy-manager ./policy-manager
|
||||||
|
|
||||||
|
# ---- Step 4: Create Go workspace inside container ----
|
||||||
|
RUN go work init ./policy-manager ./libshared
|
||||||
|
|
||||||
|
## Optional: verify workspace
|
||||||
|
#RUN go work list
|
||||||
|
|
||||||
|
# ---- Step 5: Build binary ----
|
||||||
|
WORKDIR /app/policy-manager
|
||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server
|
||||||
|
|
||||||
# ---- Runtime Stage ----
|
# ---- Runtime Stage ----
|
||||||
FROM gcr.io/distroless/base-debian12
|
FROM gcr.io/distroless/base-debian12
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
COPY --from=builder /app/policy-manager/server .
|
||||||
|
|
||||||
# Copy only the binary from builder
|
|
||||||
COPY --from=builder /app/server .
|
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
# Run as non-root user
|
|
||||||
USER nonroot:nonroot
|
USER nonroot:nonroot
|
||||||
|
|
||||||
ENTRYPOINT ["/app/server"]
|
ENTRYPOINT ["/app/server"]
|
||||||
|
|
||||||
|
|||||||
199
create-policy.go
Normal file
199
create-policy.go
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"libshared"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Custom claims struct
|
||||||
|
type MyClaims struct {
|
||||||
|
Sub string `json:"sub"`
|
||||||
|
Purpose string `json:"purpose"`
|
||||||
|
Account string `json:"account"`
|
||||||
|
jwt.RegisteredClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
type PolicyRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
PolicyDocument Policy `json:"policydocument"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PolicyResponse struct {
|
||||||
|
// PolicyID string `json:"policy_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreatePolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
log.Println("Create Policy Request")
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
// Only allow POST method
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
log.Println("Invalid method:", r.Method)
|
||||||
|
//w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
|
||||||
|
apiresponse := libshared.NewAPIResponse[PolicyResponse]("fail", "Method not allowed", PolicyResponse{})
|
||||||
|
json.NewEncoder(w).Encode(apiresponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Optional: enforce content type
|
||||||
|
if r.Header.Get("Content-Type") != "application/json" {
|
||||||
|
log.Println("Invalid Content-Type:", r.Header.Get("Content-Type"))
|
||||||
|
//w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusUnsupportedMediaType)
|
||||||
|
apiresponse := libshared.NewAPIResponse[PolicyResponse]("fail", "Content-Type must be application/json", PolicyResponse{})
|
||||||
|
json.NewEncoder(w).Encode(apiresponse)
|
||||||
|
//http.Error(w, "Content-Type must be application/json", http.StatusUnsupportedMediaType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get JWT from Authorization header
|
||||||
|
authHeader := r.Header.Get("Authorization")
|
||||||
|
if authHeader == "" {
|
||||||
|
log.Println("Missing Authorization header")
|
||||||
|
//w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
apiresponse := libshared.NewAPIResponse[PolicyResponse]("fail", "Missing Authorization header", PolicyResponse{})
|
||||||
|
json.NewEncoder(w).Encode(apiresponse)
|
||||||
|
//http.Error(w, "Missing Authorization header", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(authHeader, " ", 2)
|
||||||
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||||
|
log.Println("Invalid Authorization header format")
|
||||||
|
//w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
apiresponse := libshared.NewAPIResponse[PolicyResponse]("fail", "Invalid Authorization format", PolicyResponse{})
|
||||||
|
json.NewEncoder(w).Encode(apiresponse)
|
||||||
|
//http.Error(w, "Invalid Authorization format", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenString := parts[1]
|
||||||
|
|
||||||
|
// Replace with your actual secret or keyfunc
|
||||||
|
secret := []byte("super-secret-key")
|
||||||
|
|
||||||
|
claims := &MyClaims{}
|
||||||
|
|
||||||
|
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
// Validate signing method if needed
|
||||||
|
return secret, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil || !token.Valid {
|
||||||
|
log.Println("Invalid token:", err)
|
||||||
|
//http.Error(w, "Invalid token", http.StatusUnauthorized)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
apiresponse := libshared.NewAPIResponse[PolicyResponse]("fail", "Invalid token", PolicyResponse{})
|
||||||
|
json.NewEncoder(w).Encode(apiresponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access parsed values
|
||||||
|
log.Println("sub:", claims.Sub)
|
||||||
|
log.Println("purpose:", claims.Purpose)
|
||||||
|
log.Println("account:", claims.Account)
|
||||||
|
// exp and iat come from RegisteredClaims
|
||||||
|
log.Println("exp:", claims.ExpiresAt)
|
||||||
|
log.Println("iat:", claims.IssuedAt)
|
||||||
|
|
||||||
|
// 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 PolicyRequest
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
decoder.DisallowUnknownFields() // strict mode - reject unknown fields
|
||||||
|
|
||||||
|
if err := decoder.Decode(&policydocumentrequest); err != nil {
|
||||||
|
switch {
|
||||||
|
case err == io.EOF:
|
||||||
|
log.Println("Empty request body")
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
apiresponse := libshared.NewAPIResponse[PolicyResponse]("fail", "Empty request body", PolicyResponse{})
|
||||||
|
json.NewEncoder(w).Encode(apiresponse)
|
||||||
|
//http.Error(w, "Empty request body", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
case err.Error() == "http: request body too large":
|
||||||
|
log.Println("Request body too large")
|
||||||
|
w.WriteHeader(http.StatusRequestEntityTooLarge)
|
||||||
|
apiresponse := libshared.NewAPIResponse[PolicyResponse]("fail", "Request body too large (max 1MB)", PolicyResponse{})
|
||||||
|
json.NewEncoder(w).Encode(apiresponse)
|
||||||
|
//http.Error(w, "Request body too large (max 1MB)", http.StatusRequestEntityTooLarge)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
log.Println("Invalid JSON:", err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
apiresponse := libshared.NewAPIResponse[PolicyResponse]("fail", fmt.Sprintf("Invalid JSON: %v", err), PolicyResponse{})
|
||||||
|
json.NewEncoder(w).Encode(apiresponse)
|
||||||
|
//http.Error(w, fmt.Sprintf("Invalid JSON: %v", err), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkExisting := libshared.Pool.QueryRow(
|
||||||
|
context.Background(),
|
||||||
|
`SELECT FROM policies WHERE name=$1 AND accountid=$2`,
|
||||||
|
policydocumentrequest.Name,
|
||||||
|
claims.Account,
|
||||||
|
)
|
||||||
|
err = checkExisting.Scan()
|
||||||
|
if err == nil {
|
||||||
|
log.Println("Policy with this name already exists for this account")
|
||||||
|
w.WriteHeader(http.StatusConflict)
|
||||||
|
apiresponse := libshared.NewAPIResponse[PolicyResponse]("fail", "Policy with this name already exists", PolicyResponse{})
|
||||||
|
json.NewEncoder(w).Encode(apiresponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = libshared.Pool.Exec(
|
||||||
|
context.Background(),
|
||||||
|
`INSERT INTO policies (name, accountid, description, document) VALUES ($1, $2, $3, $4)`,
|
||||||
|
policydocumentrequest.Name,
|
||||||
|
claims.Account,
|
||||||
|
policydocumentrequest.Description,
|
||||||
|
policydocumentrequest.PolicyDocument,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Database error:", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
apiresponse := libshared.NewAPIResponse[PolicyResponse]("fail", "Internal server error", PolicyResponse{})
|
||||||
|
json.NewEncoder(w).Encode(apiresponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success response
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
|
||||||
|
apiresponse := libshared.NewAPIResponse[PolicyResponse]("success", "Policy created", PolicyResponse{})
|
||||||
|
json.NewEncoder(w).Encode(apiresponse)
|
||||||
|
|
||||||
|
// 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()),
|
||||||
|
// }
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
113
createpolicy.go
113
createpolicy.go
@@ -1,113 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
5
go.mod
5
go.mod
@@ -2,7 +2,10 @@ module policy-manager
|
|||||||
|
|
||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require github.com/jackc/pgx/v5 v5.8.0
|
require (
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||||
|
github.com/jackc/pgx/v5 v5.8.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -1,6 +1,8 @@
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
|||||||
25
main.go
25
main.go
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"libshared"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@@ -8,20 +9,28 @@ import (
|
|||||||
// Document represents the expected JSON structure
|
// Document represents the expected JSON structure
|
||||||
// You can modify this struct to match your actual data needs
|
// You can modify this struct to match your actual data needs
|
||||||
type Policy struct {
|
type Policy struct {
|
||||||
PolicyIdentifier string `json:"pid"`
|
Name string `json:"name"`
|
||||||
Comment string `json:"comment"`
|
Description string `json:"description"`
|
||||||
Effect string `json:"effect"`
|
Conditions []Condition `json:"conditions"`
|
||||||
Actions []string `json:"actions"`
|
}
|
||||||
Resources []string `json:"resources"`
|
|
||||||
|
// Condition represents each item in the "conditions" array
|
||||||
|
type Condition struct {
|
||||||
|
StatementID string `json:"statementid"`
|
||||||
|
Principal []string `json:"principals"`
|
||||||
|
Actions []string `json:"actions"`
|
||||||
|
Source []string `json:"source"`
|
||||||
|
Effect string `json:"effect"`
|
||||||
|
Operator string `json:"operator"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
pool = getDbPool()
|
pool = libshared.GetDbPool()
|
||||||
log.Println("Policy Manager service started")
|
log.Println("Policy Manager service started")
|
||||||
|
|
||||||
http.HandleFunc("/iam/create-policy", CreatePolicy)
|
http.HandleFunc("/policy/create-policy", CreatePolicy)
|
||||||
http.HandleFunc("/iam/list-policy", ListPolicy)
|
http.HandleFunc("/policy/list-policy", ListPolicy)
|
||||||
log.Println("Server running on :8080")
|
log.Println("Server running on :8080")
|
||||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user