diff --git a/cmd/seed/main.go b/cmd/seed/main.go new file mode 100644 index 0000000..0cb2ac9 --- /dev/null +++ b/cmd/seed/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "os" + + "github.com/ubcesports/echo-base/config" + "github.com/ubcesports/echo-base/internal/database" + "github.com/ubcesports/echo-base/internal/handlers" +) + +func main() { + config.LoadEnv(".env") + + database.Init() + defer database.Close() + + if len(os.Args) < 3 { + println("please specify operation and value") + os.Exit(1) + } + + if os.Args[1] != "apikey" { + println("operation not supported") + os.Exit(1) + } + + response, err := handlers.CreateApiKey(os.Args[2]) + if err != nil { + println("error while generating api key:", err.Error()) + os.Exit(1) + } + + println("generated api key!") + println("key id:", response.KeyID) + println("token:", response.APIKey) +} diff --git a/internal/handlers/keygeneration.go b/internal/handlers/keygeneration.go index ff02608..255c431 100644 --- a/internal/handlers/keygeneration.go +++ b/internal/handlers/keygeneration.go @@ -15,7 +15,7 @@ import ( ) const ( - KeyIDLength = 5 + KeyIDLength = 6 SecretLength = 32 APIKeyPrefix = "api_" MaxAppNameLength = 100 @@ -35,36 +35,18 @@ type GenerateKeyResponse struct { AppName string `json:"app_name"` } -func GenerateAPIKey(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - var req GenerateKeyRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, "Invalid request body", http.StatusBadRequest) - return - } - - if err := validateAppName(req.AppName); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - +func CreateApiKey(appName string) (res GenerateKeyResponse, err error) { keyIDBytes := make([]byte, KeyIDLength) - _, err := rand.Read(keyIDBytes) + _, err = rand.Read(keyIDBytes) if err != nil { - http.Error(w, "Failed to generate key ID", http.StatusInternalServerError) - return + return GenerateKeyResponse{}, err } keyID := strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(keyIDBytes)) secretBytes := make([]byte, SecretLength) _, err = rand.Read(secretBytes) if err != nil { - http.Error(w, "Failed to generate secret", http.StatusInternalServerError) - return + return GenerateKeyResponse{}, err } secret := base64.RawURLEncoding.EncodeToString(secretBytes) @@ -72,29 +54,56 @@ func GenerateAPIKey(w http.ResponseWriter, r *http.Request) { hasher.Write([]byte(secret)) hashedSecret := hasher.Sum(nil) - if err := storeAPIKey(req, keyID, hashedSecret); err != nil { - http.Error(w, "Failed to store API Key", http.StatusInternalServerError) - return + if err := storeAPIKey(appName, keyID, hashedSecret); err != nil { + return GenerateKeyResponse{}, err } fullKey := fmt.Sprintf("api_%s.%s", keyID, secret) + response := GenerateKeyResponse{ KeyID: keyID, APIKey: fullKey, - AppName: req.AppName, + AppName: appName, } + + return response, nil +} + +func GenerateAPIKey(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + var req GenerateKeyRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + if err := validateAppName(req.AppName); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + response, err := CreateApiKey(req.AppName) + if err != nil { + http.Error(w, "Error while generating API key", http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) } -func storeAPIKey(req GenerateKeyRequest, keyID string, hashedSecret []byte) error { +func storeAPIKey(appName string, keyID string, hashedSecret []byte) error { query := ` INSERT INTO application (app_name, key_id, hashed_key) - VALUES ($1, $2, $3) + VALUES ($1, $2, $3) ` - _, err := database.DB.Exec(query, req.AppName, keyID, hashedSecret) + _, err := database.DB.Exec(query, appName, keyID, hashedSecret) if err != nil { - return fmt.Errorf("database storage failed") + return fmt.Errorf("database storage failed: %s", err.Error()) } return nil