hatecomputers.club/api/dns/dns.go

175 lines
5.4 KiB
Go
Raw Normal View History

2024-04-03 19:53:50 -04:00
package dns
2024-03-28 00:55:22 -04:00
import (
"database/sql"
"fmt"
2024-03-28 00:55:22 -04:00
"log"
"net/http"
"strconv"
"strings"
2024-03-28 00:55:22 -04:00
2024-04-03 16:27:55 -04:00
"git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters"
2024-04-03 19:53:50 -04:00
"git.hatecomputers.club/hatecomputers/hatecomputers.club/api/types"
2024-03-28 00:55:22 -04:00
"git.hatecomputers.club/hatecomputers/hatecomputers.club/database"
"git.hatecomputers.club/hatecomputers/hatecomputers.club/utils"
2024-03-28 00:55:22 -04:00
)
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
}
2024-04-03 19:53:50 -04:00
func ListDNSRecordsContinuation(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain {
return func(success types.Continuation, failure types.Continuation) types.ContinuationChain {
2024-03-28 00:55:22 -04:00
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)
}
}
2024-04-04 17:08:50 -04:00
func CreateDNSRecordContinuation(dnsAdapter external_dns.ExternalDNSAdapter, maxUserRecords int, allowedUserDomainFormats []string) func(context *types.RequestContext, req *http.Request, resp http.ResponseWriter) types.ContinuationChain {
2024-04-03 19:53:50 -04:00
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{
2024-04-03 16:27:55 -04:00
Errors: []string{},
}
2024-04-04 17:08:50 -04:00
internal := req.FormValue("internal") == "on" || req.FormValue("internal") == "true"
2024-04-03 16:27:55 -04:00
name := req.FormValue("name")
if internal && !strings.HasSuffix(name, ".") {
name += "."
}
2024-04-03 16:27:55 -04:00
recordType := req.FormValue("type")
recordType = strings.ToUpper(recordType)
2024-04-03 16:27:55 -04:00
recordContent := req.FormValue("content")
ttl := req.FormValue("ttl")
ttlNum, err := strconv.Atoi(ttl)
if err != nil {
2024-04-04 17:08:50 -04:00
resp.WriteHeader(http.StatusBadRequest)
2024-04-03 16:27:55 -04:00
formErrors.Errors = append(formErrors.Errors, "invalid ttl")
}
2024-04-03 16:27:55 -04:00
dnsRecordCount, err := database.CountUserDNSRecords(context.DBConn, context.User.ID)
if err != nil {
log.Println(err)
resp.WriteHeader(http.StatusInternalServerError)
return failure(context, req, resp)
}
2024-04-04 17:08:50 -04:00
if dnsRecordCount >= maxUserRecords {
resp.WriteHeader(http.StatusTooManyRequests)
2024-04-03 16:27:55 -04:00
formErrors.Errors = append(formErrors.Errors, "max records reached")
}
2024-04-03 16:27:55 -04:00
dnsRecord := &database.DNSRecord{
UserID: context.User.ID,
Name: name,
Type: recordType,
Content: recordContent,
TTL: ttlNum,
Internal: internal,
}
2024-04-04 17:08:50 -04:00
if !userCanFuckWithDNSRecord(context.DBConn, context.User, dnsRecord, allowedUserDomainFormats) {
resp.WriteHeader(http.StatusUnauthorized)
2024-04-03 16:27:55 -04:00
formErrors.Errors = append(formErrors.Errors, "'name' must end with "+context.User.Username+" or you must be a domain owner for internal domains")
}
2024-04-03 16:27:55 -04:00
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)
2024-04-04 17:08:50 -04:00
resp.WriteHeader(http.StatusInternalServerError)
2024-04-03 16:27:55 -04:00
formErrors.Errors = append(formErrors.Errors, err.Error())
}
}
}
if len(formErrors.Errors) == 0 {
_, err := database.SaveDNSRecord(context.DBConn, dnsRecord)
if err != nil {
log.Println(err)
2024-04-03 16:27:55 -04:00
formErrors.Errors = append(formErrors.Errors, "error saving record")
}
}
2024-04-03 16:27:55 -04:00
if len(formErrors.Errors) == 0 {
return success(context, req, resp)
}
2024-04-03 16:27:55 -04:00
(*context.TemplateData)["FormError"] = &formErrors
(*context.TemplateData)["RecordForm"] = dnsRecord
return failure(context, req, resp)
}
}
}
2024-04-03 19:53:50 -04:00
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 {
2024-04-03 16:27:55 -04:00
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)
}
2024-04-04 17:08:50 -04:00
if !(record.UserID == context.User.ID) {
2024-04-03 16:27:55 -04:00
resp.WriteHeader(http.StatusUnauthorized)
return failure(context, req, resp)
}
2024-04-03 16:27:55 -04:00
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)
}
2024-04-03 16:27:55 -04:00
return success(context, req, resp)
}
2024-03-28 00:55:22 -04:00
}
}