summaryrefslogtreecommitdiff
path: root/api/dns
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth@simponic.xyz>2024-04-03 17:53:50 -0600
committerElizabeth Hunt <elizabeth@simponic.xyz>2024-04-03 17:53:50 -0600
commitf38e8719c2a8537fe9b64ed8ceca45858a58e498 (patch)
tree5cf2c7c7f6396f75bdb841db00638e4eef8e81e8 /api/dns
parente398cf05402c010d594cea4e2dea307ca1a36dbe (diff)
downloadhatecomputers.club-f38e8719c2a8537fe9b64ed8ceca45858a58e498.tar.gz
hatecomputers.club-f38e8719c2a8537fe9b64ed8ceca45858a58e498.zip
make it compile
Diffstat (limited to 'api/dns')
-rw-r--r--api/dns/dns.go178
-rw-r--r--api/dns/dns_test.go63
2 files changed, 241 insertions, 0 deletions
diff --git a/api/dns/dns.go b/api/dns/dns.go
new file mode 100644
index 0000000..4805146
--- /dev/null
+++ b/api/dns/dns.go
@@ -0,0 +1,178 @@
+package dns
+
+import (
+ "database/sql"
+ "fmt"
+ "log"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters"
+ "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/types"
+ "git.hatecomputers.club/hatecomputers/hatecomputers.club/database"
+ "git.hatecomputers.club/hatecomputers/hatecomputers.club/utils"
+)
+
+const MAX_USER_RECORDS = 65
+
+var USER_OWNED_INTERNAL_FMT_DOMAINS = []string{"%s", "%s.endpoints"}
+
+func userCanFuckWithDNSRecord(dbConn *sql.DB, user *database.User, record *database.DNSRecord, ownedInternalDomainFormats []string) bool {
+ ownedByUser := (user.ID == record.UserID)
+ if !ownedByUser {
+ return false
+ }
+
+ if !record.Internal {
+ for _, format := range ownedInternalDomainFormats {
+ domain := fmt.Sprintf(format, user.Username)
+
+ isInSubDomain := strings.HasSuffix(record.Name, "."+domain)
+ if domain == record.Name || isInSubDomain {
+ return true
+ }
+ }
+ return false
+ }
+
+ owner, err := database.FindFirstDomainOwnerId(dbConn, record.Name)
+ if err != nil {
+ log.Println(err)
+ return false
+ }
+
+ userIsOwnerOfDomain := owner == user.ID
+ return ownedByUser && userIsOwnerOfDomain
+}
+
+func ListDNSRecordsContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain {
+ return func(success types.Continuation, failure types.Continuation) types.ContinuationChain {
+ dnsRecords, err := database.GetUserDNSRecords(context.DBConn, context.User.ID)
+ if err != nil {
+ log.Println(err)
+ resp.WriteHeader(http.StatusInternalServerError)
+ return failure(context, req, resp)
+ }
+
+ (*context.TemplateData)["DNSRecords"] = dnsRecords
+ return success(context, req, resp)
+ }
+}
+
+func CreateDNSRecordContinuation(dnsAdapter external_dns.ExternalDNSAdapter) func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain {
+ return func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain {
+ return func(success types.Continuation, failure types.Continuation) types.ContinuationChain {
+ formErrors := types.FormError{
+ Errors: []string{},
+ }
+
+ internal := req.FormValue("internal") == "on"
+ name := req.FormValue("name")
+ if internal && !strings.HasSuffix(name, ".") {
+ name += "."
+ }
+
+ recordType := req.FormValue("type")
+ recordType = strings.ToUpper(recordType)
+
+ recordContent := req.FormValue("content")
+ ttl := req.FormValue("ttl")
+ ttlNum, err := strconv.Atoi(ttl)
+ if err != nil {
+ formErrors.Errors = append(formErrors.Errors, "invalid ttl")
+ }
+
+ dnsRecordCount, err := database.CountUserDNSRecords(context.DBConn, context.User.ID)
+ if err != nil {
+ log.Println(err)
+ resp.WriteHeader(http.StatusInternalServerError)
+ return failure(context, req, resp)
+ }
+ if dnsRecordCount >= MAX_USER_RECORDS {
+ formErrors.Errors = append(formErrors.Errors, "max records reached")
+ }
+
+ dnsRecord := &database.DNSRecord{
+ UserID: context.User.ID,
+ Name: name,
+ Type: recordType,
+ Content: recordContent,
+ TTL: ttlNum,
+ Internal: internal,
+ }
+
+ if !userCanFuckWithDNSRecord(context.DBConn, context.User, dnsRecord, USER_OWNED_INTERNAL_FMT_DOMAINS) {
+ formErrors.Errors = append(formErrors.Errors, "'name' must end with "+context.User.Username+" or you must be a domain owner for internal domains")
+ }
+
+ if len(formErrors.Errors) == 0 {
+ if dnsRecord.Internal {
+ dnsRecord.ID = utils.RandomId()
+ } else {
+ dnsRecord.ID, err = dnsAdapter.CreateDNSRecord(dnsRecord)
+ if err != nil {
+ log.Println(err)
+ formErrors.Errors = append(formErrors.Errors, err.Error())
+ }
+ }
+ }
+
+ if len(formErrors.Errors) == 0 {
+ _, err := database.SaveDNSRecord(context.DBConn, dnsRecord)
+ if err != nil {
+ log.Println(err)
+ formErrors.Errors = append(formErrors.Errors, "error saving record")
+ }
+ }
+
+ if len(formErrors.Errors) == 0 {
+ http.Redirect(resp, req, "/dns", http.StatusFound)
+ return success(context, req, resp)
+ }
+
+ (*context.TemplateData)["FormError"] = &formErrors
+ (*context.TemplateData)["RecordForm"] = dnsRecord
+
+ resp.WriteHeader(http.StatusBadRequest)
+ return failure(context, req, resp)
+ }
+ }
+}
+
+func DeleteDNSRecordContinuation(dnsAdapter external_dns.ExternalDNSAdapter) func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain {
+ return func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain {
+ return func(success types.Continuation, failure types.Continuation) types.ContinuationChain {
+ recordId := req.FormValue("id")
+ record, err := database.GetDNSRecord(context.DBConn, recordId)
+ if err != nil {
+ log.Println(err)
+ resp.WriteHeader(http.StatusInternalServerError)
+ return failure(context, req, resp)
+ }
+
+ if !userCanFuckWithDNSRecord(context.DBConn, context.User, record, USER_OWNED_INTERNAL_FMT_DOMAINS) {
+ resp.WriteHeader(http.StatusUnauthorized)
+ return failure(context, req, resp)
+ }
+
+ if !record.Internal {
+ err = dnsAdapter.DeleteDNSRecord(recordId)
+ if err != nil {
+ log.Println(err)
+ resp.WriteHeader(http.StatusInternalServerError)
+ return failure(context, req, resp)
+ }
+ }
+
+ err = database.DeleteDNSRecord(context.DBConn, recordId)
+ if err != nil {
+ resp.WriteHeader(http.StatusInternalServerError)
+ return failure(context, req, resp)
+ }
+
+ http.Redirect(resp, req, "/dns", http.StatusFound)
+ return success(context, req, resp)
+ }
+ }
+}
diff --git a/api/dns/dns_test.go b/api/dns/dns_test.go
new file mode 100644
index 0000000..cc56120
--- /dev/null
+++ b/api/dns/dns_test.go
@@ -0,0 +1,63 @@
+package dns_test
+
+import (
+ "database/sql"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+
+ // "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/dns"
+ "git.hatecomputers.club/hatecomputers/hatecomputers.club/api/types"
+ "git.hatecomputers.club/hatecomputers/hatecomputers.club/args"
+ "git.hatecomputers.club/hatecomputers/hatecomputers.club/database"
+ "git.hatecomputers.club/hatecomputers/hatecomputers.club/utils"
+)
+
+func IdContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain {
+ return func(success types.Continuation, _failure types.Continuation) types.ContinuationChain {
+ return success(context, req, resp)
+ }
+}
+
+func setup() (*sql.DB, *types.RequestContext, func()) {
+ randomDb := utils.RandomId()
+
+ testDb := database.MakeConn(&randomDb)
+ database.Migrate(testDb)
+
+ context := &types.RequestContext{
+ DBConn: testDb,
+ Args: &args.Arguments{},
+ TemplateData: &(map[string]interface{}{}),
+ }
+
+ return testDb, context, func() {
+ testDb.Close()
+ os.Remove(randomDb)
+ }
+}
+
+func TestThatOwnerCanPutRecordInDomain(t *testing.T) {
+ db, context, cleanup := setup()
+ defer cleanup()
+
+ _ = &database.User{
+ ID: "test",
+ Username: "test",
+ }
+
+ records, err := database.GetUserDNSRecords(db, context.User.ID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(records) > 0 {
+ t.Errorf("expected no records, got records")
+ }
+
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // dns.CreateDNSRecordContinuation(context, r, w)(IdContinuation, IdContinuation)
+ }))
+ defer ts.Close()
+
+}