mirror of
https://git.freecumextremist.com/grumbulon/pomme.git
synced 2024-12-22 21:30:44 +00:00
Merge pull request 'feat: save zonefile to permanent directory' (#28) from nds into master
Reviewed-on: https://git.freecumextremist.com/grumbulon/pomme/pulls/28
This commit is contained in:
commit
24b6783fc9
16 changed files with 150 additions and 339 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -26,4 +26,6 @@ test.db
|
|||
test.sqlite
|
||||
pomme.sqlite
|
||||
.dccache
|
||||
config.yaml
|
||||
config.yaml
|
||||
|
||||
zones/*
|
|
@ -2,4 +2,5 @@ server: example.com # does nothing yet
|
|||
hashingsecret: PasswordHashingSecret
|
||||
port: 3008 # port the server runs on
|
||||
db: pomme.sqlite
|
||||
testdb: test.sqlite
|
||||
testdb: test.sqlite
|
||||
zonedir: zones
|
55
docs/docs.go
55
docs/docs.go
|
@ -1,5 +1,4 @@
|
|||
// Package docs GENERATED BY SWAG; DO NOT EDIT
|
||||
// This file was generated by swaggo/swag
|
||||
// Code generated by swaggo/swag. DO NOT EDIT
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
@ -62,56 +61,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/parse": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "parse your zonefile\nRate limited: 10 requests every 10 second\nyou must specify \"Bearer\" before entering your token",
|
||||
"consumes": [
|
||||
"multipart/form-data"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"DNS"
|
||||
],
|
||||
"summary": "parse your zonefile",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Zonefile name",
|
||||
"name": "filename",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Bearer Token",
|
||||
"name": "Authorization",
|
||||
"in": "header",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "internalServerError is a 500 server error with a logged error call back",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/upload": {
|
||||
"post": {
|
||||
"security": [
|
||||
|
@ -119,7 +68,7 @@ const docTemplate = `{
|
|||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "upload takes files from the user and stores it locally to be parsed. Uploads are associated with a specific user.\nRate limited: 10 requests every 10 second\nyou must specify \"Bearer\" before entering your token",
|
||||
"description": "Upload takes a multipart form file as user input. It must not exceed 1 mb and must be of text/plain content type.\nIf a file uploads successfully it will be saved locally and parsed.\nIf parsing is successful pomme will save the file to your ZoneDir defined in your config.\nUploads are associated with a specific user.\nRate limited: 10 requests every 10 second\nyou must specify \"Bearer\" before entering your token",
|
||||
"consumes": [
|
||||
"multipart/form-data"
|
||||
],
|
||||
|
|
|
@ -53,56 +53,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/parse": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "parse your zonefile\nRate limited: 10 requests every 10 second\nyou must specify \"Bearer\" before entering your token",
|
||||
"consumes": [
|
||||
"multipart/form-data"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"DNS"
|
||||
],
|
||||
"summary": "parse your zonefile",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Zonefile name",
|
||||
"name": "filename",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Bearer Token",
|
||||
"name": "Authorization",
|
||||
"in": "header",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "internalServerError is a 500 server error with a logged error call back",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/upload": {
|
||||
"post": {
|
||||
"security": [
|
||||
|
@ -110,7 +60,7 @@
|
|||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "upload takes files from the user and stores it locally to be parsed. Uploads are associated with a specific user.\nRate limited: 10 requests every 10 second\nyou must specify \"Bearer\" before entering your token",
|
||||
"description": "Upload takes a multipart form file as user input. It must not exceed 1 mb and must be of text/plain content type.\nIf a file uploads successfully it will be saved locally and parsed.\nIf parsing is successful pomme will save the file to your ZoneDir defined in your config.\nUploads are associated with a specific user.\nRate limited: 10 requests every 10 second\nyou must specify \"Bearer\" before entering your token",
|
||||
"consumes": [
|
||||
"multipart/form-data"
|
||||
],
|
||||
|
|
|
@ -50,48 +50,15 @@ paths:
|
|||
summary: authenticate as a regular user
|
||||
tags:
|
||||
- accounts
|
||||
/api/parse:
|
||||
post:
|
||||
consumes:
|
||||
- multipart/form-data
|
||||
description: |-
|
||||
parse your zonefile
|
||||
Rate limited: 10 requests every 10 second
|
||||
you must specify "Bearer" before entering your token
|
||||
parameters:
|
||||
- description: Zonefile name
|
||||
in: query
|
||||
name: filename
|
||||
required: true
|
||||
type: string
|
||||
- description: Bearer Token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response'
|
||||
"500":
|
||||
description: internalServerError is a 500 server error with a logged error
|
||||
call back
|
||||
schema:
|
||||
$ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: parse your zonefile
|
||||
tags:
|
||||
- DNS
|
||||
/api/upload:
|
||||
post:
|
||||
consumes:
|
||||
- multipart/form-data
|
||||
description: |-
|
||||
upload takes files from the user and stores it locally to be parsed. Uploads are associated with a specific user.
|
||||
Upload takes a multipart form file as user input. It must not exceed 1 mb and must be of text/plain content type.
|
||||
If a file uploads successfully it will be saved locally and parsed.
|
||||
If parsing is successful pomme will save the file to your ZoneDir defined in your config.
|
||||
Uploads are associated with a specific user.
|
||||
Rate limited: 10 requests every 10 second
|
||||
you must specify "Bearer" before entering your token
|
||||
parameters:
|
||||
|
|
2
go.mod
2
go.mod
|
@ -53,7 +53,7 @@ require (
|
|||
github.com/swaggo/http-swagger v1.3.3
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/net v0.5.0 // indirect
|
||||
golang.org/x/sys v0.4.0 // indirect
|
||||
golang.org/x/sys v0.4.0
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/libc v1.21.5 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -185,8 +185,6 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.3 h1:WL2ifUmzR/SLp85CSURAfybcHnGZ+yLSGSxgYXlFBHg=
|
||||
gorm.io/gorm v1.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.5 h1:g6OPREKqqlWq4kh/3MCQbZKImeB9e6Xgc4zD+JgNZGE=
|
||||
gorm.io/gorm v1.24.5/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
|
|
|
@ -34,7 +34,6 @@ func API() http.Handler {
|
|||
|
||||
api.Use(jwtauth.Authenticator)
|
||||
api.With(setDBMiddleware).Post("/upload", ReceiveFile)
|
||||
api.With(setDBMiddleware).Post("/parse", ParseZoneFiles)
|
||||
})
|
||||
|
||||
// Open routes
|
||||
|
|
|
@ -313,40 +313,6 @@ func (a *accountTest) TestUpload(t *testing.T) {
|
|||
|
||||
assert.Equal(t, tc.expected.response, resp.StatusCode)
|
||||
}
|
||||
|
||||
if tc.name == "Should upload a valid file" {
|
||||
parse(t, f.Name(), a)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func parse(t *testing.T, fname string, a *accountTest) {
|
||||
var target response
|
||||
|
||||
client := http.Client{}
|
||||
|
||||
form := url.Values{}
|
||||
|
||||
form.Add("filename", filepath.Clean(fname))
|
||||
|
||||
if req, err := http.NewRequest(http.MethodPost, a.url+`/api/parse`, strings.NewReader(form.Encode())); err == nil {
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Add("Authorization", `Bearer:`+a.token)
|
||||
req.Header.Add("User-Agent", "pomme-api-test-slave")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
|
||||
err = json.Unmarshal(respBody, &target)
|
||||
if err != nil {
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, http.StatusCreated, resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
|
57
internal/api/fs.go
Normal file
57
internal/api/fs.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"git.freecumextremist.com/grumbulon/pomme/internal"
|
||||
)
|
||||
|
||||
var errEmptyFile = errors.New("will not save empty file to FS")
|
||||
|
||||
// makeLocal takes a type path and then saves a zone file to either tmp or a permanent location.
|
||||
func makeLocal(zone *ZoneRequest) error {
|
||||
if _, err := os.Stat(fmt.Sprintf(zone.FileName, zone.User)); !os.IsNotExist(err) {
|
||||
return fmt.Errorf("file %s already exists: %w", zone.FileName, err)
|
||||
}
|
||||
|
||||
if len(zone.Body) == 0 {
|
||||
return errEmptyFile
|
||||
}
|
||||
|
||||
c, err := internal.ReadConfig()
|
||||
if err != nil {
|
||||
logHandler(genericResponseFields{"error": err.Error(), "message": "no config file defined"})
|
||||
|
||||
return fmt.Errorf("unable to parse directory: %w", err)
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/%s/", c.ZoneDir, zone.FileName)
|
||||
if err = os.MkdirAll(path, 0o750); err != nil {
|
||||
logHandler(genericResponseFields{"error": err.Error(), "message": "unable to make directory for zone files"})
|
||||
|
||||
return fmt.Errorf("unable to make zone directory: %w", err)
|
||||
}
|
||||
|
||||
f, err := os.Create(filepath.Clean(path + zone.FileName)) //nolint: gosec
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write file locally: %w", err)
|
||||
}
|
||||
|
||||
// close and remove the temporary file at the end of the program
|
||||
defer func() {
|
||||
if err = f.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
err = os.WriteFile(f.Name(), zone.Body, 0o600)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write file locally: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.freecumextremist.com/grumbulon/pomme/internal"
|
||||
|
@ -35,7 +34,7 @@ func setDBMiddleware(next http.Handler) http.Handler {
|
|||
|
||||
if err != nil && !ok {
|
||||
logHandler(genericResponseFields{"error": err.Error(), "message": "Error initializing DB"})
|
||||
err = syscall.Kill(syscall.Getpid(), syscall.SIGINT)
|
||||
err = internal.SysKill()
|
||||
if err != nil {
|
||||
panic("idk what to do with this but syscall.Kill errored out.")
|
||||
}
|
||||
|
@ -48,7 +47,7 @@ func setDBMiddleware(next http.Handler) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func APIError[T map[string]any](w http.ResponseWriter, r *http.Request, v map[string]any) {
|
||||
func APIError[T ~map[string]any](w http.ResponseWriter, r *http.Request, v T) {
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
|
|
|
@ -6,25 +6,48 @@ const (
|
|||
keyPrincipalContextID key = iota
|
||||
)
|
||||
|
||||
type genericResponseFields map[string]any
|
||||
|
||||
type key int
|
||||
|
||||
// ZoneRequest represents a Zone file request.
|
||||
// ZoneRequest represents a new zone file request.
|
||||
//
|
||||
// Inside it is a pointer to the zone struct, which contains zone file information, and a User field to keep track of whom owns the file/request.
|
||||
type ZoneRequest struct {
|
||||
*Zone
|
||||
|
||||
User string `json:"user,omitempty" gorm:"foreignKey:username;references:User"`
|
||||
}
|
||||
|
||||
// Zone struct represents a zonefile.
|
||||
// Zone struct represents a zone file.
|
||||
type Zone struct {
|
||||
gorm.Model
|
||||
FileName string `json:"name"`
|
||||
RawFileName string `json:"rawname"`
|
||||
Body string `json:"body,omitempty"`
|
||||
|
||||
// FileName is the file name for an uploaded zone file how it is expected to show up on the filesystem
|
||||
FileName string `json:"name"`
|
||||
|
||||
// Body is the bytes array of a zone files body for copying and moving it around
|
||||
Body []byte `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// GenericResponse is a generics wrapper to send responses for API Errors.
|
||||
type GenericResponse[T map[string]any] struct {
|
||||
Response map[string]any `json:"response,omitempty"`
|
||||
}
|
||||
|
||||
// instead of calling map[string]any{...} you can call genericResponseFields{...} when making a generic response above.
|
||||
type genericResponseFields map[string]any
|
||||
|
||||
// ndr is an interface for new DNS requests. It's methods can be used with a ZoneRequest object.
|
||||
type ndr interface {
|
||||
// parse() is a wrapper around miekg's NewZoneParser, which is used to validate uploaded zone files
|
||||
//
|
||||
// if no error is raised the zone file can be saved.
|
||||
parse() error
|
||||
|
||||
// save() is a wrapper around internal.makeLocal() which will save a valid non-empty zone file to the filesystem
|
||||
//
|
||||
// the file is saved to a location the user sets up in their config.yaml file,
|
||||
// once saved it can be consumed by something like NSD.
|
||||
save() error
|
||||
}
|
||||
|
||||
var _ ndr = (*ZoneRequest)(nil)
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.freecumextremist.com/grumbulon/pomme/internal"
|
||||
"git.freecumextremist.com/grumbulon/pomme/internal/util"
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/miekg/dns"
|
||||
|
@ -20,7 +17,10 @@ import (
|
|||
// Upload godoc
|
||||
//
|
||||
// @Summary upload a zonefile
|
||||
// @Description upload takes files from the user and stores it locally to be parsed. Uploads are associated with a specific user.
|
||||
// @Description Upload takes a multipart form file as user input. It must not exceed 1 mb and must be of text/plain content type.
|
||||
// @Description If a file uploads successfully it will be saved locally and parsed.
|
||||
// @Description If parsing is successful pomme will save the file to your ZoneDir defined in your config.
|
||||
// @Description Uploads are associated with a specific user.
|
||||
//
|
||||
// @Description Rate limited: 10 requests every 10 second
|
||||
// @Description you must specify "Bearer" before entering your token
|
||||
|
@ -39,8 +39,6 @@ import (
|
|||
func ReceiveFile(w http.ResponseWriter, r *http.Request) {
|
||||
_, claims, _ := jwtauth.FromContext(r.Context())
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
r.Body = http.MaxBytesReader(w, r.Body, 1*1024*1024) // approx 1 mb max upload
|
||||
|
||||
file, header, err := r.FormFile("file")
|
||||
|
@ -52,23 +50,24 @@ func ReceiveFile(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
defer file.Close() //nolint: errcheck
|
||||
|
||||
b, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
APIError(w, r, genericResponseFields{"message": "internal server error", "status": http.StatusInternalServerError, "error": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ok := validateContentType(file)
|
||||
if !ok {
|
||||
http.Error(w, "file must be text/plain", http.StatusUnsupportedMediaType)
|
||||
APIError(w, r, genericResponseFields{"message": "file must be text/plain", "status": http.StatusUnsupportedMediaType})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
name := strings.Split(header.Filename, ".")
|
||||
zoneFile := newDNSRequest(header.Filename, claims["username"].(string), b)
|
||||
|
||||
if _, err = io.Copy(&buf, file); err != nil {
|
||||
APIError(w, r, genericResponseFields{"message": "internal server error", "status": http.StatusInternalServerError, "error": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err = util.MakeLocal(name[0], claims["username"].(string), buf); err != nil {
|
||||
APIError(w, r, genericResponseFields{"message": "internal server error", "status": http.StatusInternalServerError, "error": err.Error()})
|
||||
if err := zoneFile.parse(); err != nil {
|
||||
APIError(w, r, genericResponseFields{"message": "Unable to parse zonefile", "status": http.StatusInternalServerError, "error": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -84,12 +83,15 @@ func ReceiveFile(w http.ResponseWriter, r *http.Request) {
|
|||
&ZoneRequest{
|
||||
User: claims["username"].(string),
|
||||
Zone: &Zone{
|
||||
FileName: fmt.Sprintf("tmpfile-%s-%s", name[0], claims["username"].(string)),
|
||||
RawFileName: name[0],
|
||||
FileName: header.Filename,
|
||||
},
|
||||
})
|
||||
|
||||
buf.Reset()
|
||||
if err := zoneFile.save(); err != nil {
|
||||
APIError(w, r, genericResponseFields{"message": "Unable to save zonefile", "status": http.StatusInternalServerError, "error": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
|
@ -102,103 +104,18 @@ func ReceiveFile(w http.ResponseWriter, r *http.Request) {
|
|||
render.JSON(w, r, resp)
|
||||
}
|
||||
|
||||
// Parse godoc
|
||||
//
|
||||
// @Summary parse your zonefile
|
||||
// @Description parse your zonefile
|
||||
//
|
||||
// @Description Rate limited: 10 requests every 10 second
|
||||
// @Description you must specify "Bearer" before entering your token
|
||||
//
|
||||
// @Tags DNS
|
||||
// @Accept mpfd
|
||||
// @Produce json
|
||||
// @Param filename query string true "Zonefile name"
|
||||
// @Success 200 {object} internal.SwaggerGenericResponse[internal.Response]
|
||||
// @Failure 500 {object} internal.SwaggerGenericResponse[internal.Response] "internalServerError is a 500 server error with a logged error call back"
|
||||
// @Param Authorization header string true "Bearer Token"
|
||||
//
|
||||
// @Security Bearer
|
||||
//
|
||||
// @Router /api/parse [post]
|
||||
func ParseZoneFiles(w http.ResponseWriter, r *http.Request) {
|
||||
var result internal.ZoneRequest
|
||||
|
||||
_, claims, _ := jwtauth.FromContext(r.Context())
|
||||
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
APIError(w, r, genericResponseFields{"message": "internal server error", "status": http.StatusInternalServerError, "error": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filename := r.Form.Get("filename")
|
||||
|
||||
if filename == "" {
|
||||
APIError(w, r, genericResponseFields{"message": "no filename provided", "status": http.StatusInternalServerError})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
db, ok := r.Context().Value(keyPrincipalContextID).(*gorm.DB)
|
||||
if !ok {
|
||||
APIError(w, r, genericResponseFields{"message": "internal server error", "status": http.StatusInternalServerError, "error": "unable to connect to DB"})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
db.Where(ZoneRequest{
|
||||
Zone: &Zone{
|
||||
RawFileName: filename,
|
||||
},
|
||||
User: claims["username"].(string),
|
||||
}).First(&result)
|
||||
|
||||
if result == (internal.ZoneRequest{}) {
|
||||
APIError(w, r, genericResponseFields{"message": "internal server error", "status": http.StatusInternalServerError})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
zoneFile := newZoneRequest(result.RawFileName, claims["username"].(string))
|
||||
|
||||
if err := zoneFile.Parse(); err != nil {
|
||||
APIError(w, r, genericResponseFields{"message": "Unable to parse zonefile", "status": http.StatusInternalServerError, "error": err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
resp := internal.Response{
|
||||
Message: "Successfully parsed zonefile",
|
||||
}
|
||||
|
||||
render.JSON(w, r, resp)
|
||||
}
|
||||
|
||||
func newZoneRequest(filename string, user string) *ZoneRequest {
|
||||
dat, err := os.ReadFile(fmt.Sprintf("/tmp/tmpfile-%s-%s", filename, user))
|
||||
if err != nil {
|
||||
return &ZoneRequest{}
|
||||
}
|
||||
|
||||
func newDNSRequest(filename string, user string, dat []byte) ndr {
|
||||
return &ZoneRequest{
|
||||
User: user,
|
||||
Zone: &Zone{
|
||||
FileName: fmt.Sprintf("tmpfile-%s-%s", filename, user),
|
||||
RawFileName: filename,
|
||||
Body: string(dat),
|
||||
FileName: filename,
|
||||
Body: dat,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Parse will be used to parse zonefiles.
|
||||
func (zone *ZoneRequest) Parse() error {
|
||||
zp := dns.NewZoneParser(strings.NewReader(zone.Body), "", "")
|
||||
func (zone *ZoneRequest) parse() error {
|
||||
zp := dns.NewZoneParser(strings.NewReader(string(zone.Body)), "", "")
|
||||
|
||||
for rr, ok := zp.Next(); ok; rr, ok = zp.Next() {
|
||||
log.Println(rr)
|
||||
|
@ -211,6 +128,10 @@ func (zone *ZoneRequest) Parse() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (zone *ZoneRequest) save() error {
|
||||
return makeLocal(zone)
|
||||
}
|
||||
|
||||
func validateContentType(file io.Reader) bool {
|
||||
bytes, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
|
|
15
internal/sys.go
Normal file
15
internal/sys.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package internal
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func SysKill() (err error) {
|
||||
err = killPomme()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func killPomme() (err error) {
|
||||
err = unix.Kill(unix.Getpid(), unix.SIGINT)
|
||||
|
||||
return
|
||||
}
|
|
@ -24,9 +24,8 @@ type ZoneRequest struct {
|
|||
// Zone struct represents a zonefile in the database.
|
||||
type Zone struct {
|
||||
gorm.Model
|
||||
FileName string `json:"name"`
|
||||
RawFileName string `json:"rawfilename"`
|
||||
Body string `json:"body"`
|
||||
FileName string `json:"name"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
|
@ -35,6 +34,7 @@ type Config struct {
|
|||
Port string
|
||||
DB string
|
||||
TestDB string
|
||||
ZoneDir string
|
||||
}
|
||||
|
||||
// SwaggerGenericResponse[T]
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func MakeLocal(filename, username string, buf bytes.Buffer) error {
|
||||
if _, err := os.Stat(fmt.Sprintf("/tmp/tmpfile-%s-%s", filename, username)); !os.IsNotExist(err) {
|
||||
return fmt.Errorf("file %s already exists: %w", filename, err)
|
||||
}
|
||||
|
||||
defer buf.Reset()
|
||||
|
||||
f, err := os.Create("/tmp/tmpfile-" + filename + "-" + username) //nolint: gosec
|
||||
// this is set to nolint because I am doing everything os.CreateTemp does but here since I don't like some of the limitations
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write file locally: %w", err)
|
||||
}
|
||||
|
||||
// close and remove the temporary file at the end of the program
|
||||
defer func() {
|
||||
if err = f.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
err = os.WriteFile(f.Name(), buf.Bytes(), 0o600)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write file locally: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue