2024-04-03 19:53:50 -04:00
package dns
2024-03-28 00:55:22 -04:00
import (
2024-03-28 16:58:07 -04:00
"database/sql"
2024-04-02 16:33:11 -04:00
"fmt"
2024-03-28 00:55:22 -04:00
"log"
"net/http"
2024-03-28 12:57:35 -04:00
"strconv"
"strings"
2024-03-28 00:55:22 -04:00
2024-04-09 18:39:14 -04:00
"git.hatecomputers.club/hatecomputers/hatecomputers.club/adapters/external_dns"
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"
2024-03-28 16:58:07 -04:00
"git.hatecomputers.club/hatecomputers/hatecomputers.club/utils"
2024-03-28 00:55:22 -04:00
)
2024-04-09 18:39:14 -04:00
const MaxUserRecords = 100
2024-03-28 16:58:07 -04:00
2024-04-09 18:39:14 -04:00
var UserOwnedInternalFmtDomains = [ ] string { "%s" , "%s.endpoints" }
2024-03-28 12:57:35 -04:00
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-05-12 17:32:50 -04:00
if context . User == nil {
return failure ( context , req , resp )
}
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
2024-03-28 12:57:35 -04:00
return success ( context , req , resp )
}
}
2024-06-24 03:18:28 -04:00
func CreateDNSRecordContinuation ( externalDnsAdapter 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 {
2024-04-09 18:39:14 -04:00
formErrors := types . BannerMessages {
Messages : [ ] string { } ,
2024-04-03 16:27:55 -04:00
}
2024-03-28 12:57:35 -04:00
2024-06-24 03:18:28 -04:00
dnsRecord := & database . DNSRecord { }
id := req . FormValue ( "id" )
isNewRecord := id == ""
if ! isNewRecord {
retrievedDnsRecord , err := database . GetDNSRecord ( context . DBConn , id )
if err != nil {
log . Println ( err )
resp . WriteHeader ( http . StatusInternalServerError )
formErrors . Messages = append ( formErrors . Messages , "error getting record from id" )
} else {
dnsRecord = retrievedDnsRecord
}
} else {
dnsRecord . UserID = context . User . ID
}
dnsRecord . Internal = req . FormValue ( "internal" ) == "on" || req . FormValue ( "internal" ) == "true"
dnsRecord . Name = req . FormValue ( "name" )
if dnsRecord . Internal && ! strings . HasSuffix ( dnsRecord . Name , "." ) {
dnsRecord . Name += "."
2024-04-03 16:27:55 -04:00
}
2024-03-28 16:58:07 -04:00
2024-04-03 16:27:55 -04:00
recordType := req . FormValue ( "type" )
2024-06-24 03:18:28 -04:00
dnsRecord . Type = strings . ToUpper ( recordType )
dnsRecord . Content = req . FormValue ( "content" )
2024-03-28 16:58:07 -04:00
2024-04-03 16:27:55 -04:00
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-09 18:39:14 -04:00
formErrors . Messages = append ( formErrors . Messages , "invalid ttl" )
2024-04-03 16:27:55 -04:00
}
2024-06-24 03:18:28 -04:00
dnsRecord . TTL = ttlNum
2024-03-28 12:57:35 -04:00
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-09 18:39:14 -04:00
formErrors . Messages = append ( formErrors . Messages , "max records reached" )
2024-04-03 16:27:55 -04:00
}
2024-03-28 12:57:35 -04:00
2024-06-24 03:18:28 -04:00
if len ( formErrors . Messages ) == 0 && ! userCanFuckWithDNSRecord ( context . DBConn , context . User , dnsRecord , allowedUserDomainFormats ) {
2024-04-04 17:08:50 -04:00
resp . WriteHeader ( http . StatusUnauthorized )
2024-06-24 03:18:28 -04:00
formErrors . Messages = append ( formErrors . Messages , "external '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
}
2024-03-28 12:57:35 -04:00
2024-06-24 03:18:28 -04:00
if isNewRecord && len ( formErrors . Messages ) == 0 {
2024-04-03 16:27:55 -04:00
if dnsRecord . Internal {
dnsRecord . ID = utils . RandomId ( )
} else {
2024-06-24 03:18:28 -04:00
dnsRecord . ID , err = externalDnsAdapter . CreateDNSRecord ( dnsRecord )
if err != nil {
log . Println ( "error creating external dns record" , err )
resp . WriteHeader ( http . StatusInternalServerError )
formErrors . Messages = append ( formErrors . Messages , err . Error ( ) )
}
}
}
if ! isNewRecord && len ( formErrors . Messages ) == 0 {
if ! dnsRecord . Internal {
err = externalDnsAdapter . UpdateDNSRecord ( dnsRecord )
2024-04-03 16:27:55 -04:00
if err != nil {
2024-06-24 03:18:28 -04:00
log . Println ( "error updating external dns record" , err )
2024-04-04 17:08:50 -04:00
resp . WriteHeader ( http . StatusInternalServerError )
2024-04-09 18:39:14 -04:00
formErrors . Messages = append ( formErrors . Messages , err . Error ( ) )
2024-04-03 16:27:55 -04:00
}
}
}
2024-04-09 18:39:14 -04:00
if len ( formErrors . Messages ) == 0 {
2024-04-03 16:27:55 -04:00
_ , err := database . SaveDNSRecord ( context . DBConn , dnsRecord )
2024-03-28 16:58:07 -04:00
if err != nil {
log . Println ( err )
2024-04-09 18:39:14 -04:00
formErrors . Messages = append ( formErrors . Messages , "error saving record" )
2024-03-28 16:58:07 -04:00
}
2024-03-28 12:57:35 -04:00
}
2024-04-09 18:39:14 -04:00
if len ( formErrors . Messages ) == 0 {
formSuccess := types . BannerMessages {
2024-06-24 03:18:28 -04:00
Messages : [ ] string { "record saved." } ,
2024-04-09 18:39:14 -04:00
}
( * context . TemplateData ) [ "Success" ] = formSuccess
2024-04-03 16:27:55 -04:00
return success ( context , req , resp )
2024-03-28 12:57:35 -04:00
}
2024-06-24 03:18:28 -04:00
log . Println ( formErrors . Messages )
( * context . TemplateData ) [ "Error" ] = & formErrors
2024-04-03 16:27:55 -04:00
( * context . TemplateData ) [ "RecordForm" ] = dnsRecord
return failure ( context , req , resp )
}
2024-03-28 12:57:35 -04:00
}
}
2024-06-24 03:18:28 -04:00
func DeleteDNSRecordContinuation ( externalDnsAdapter external_dns . ExternalDNSAdapter ) 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 {
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-03-28 12:57:35 -04:00
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-03-28 12:57:35 -04:00
2024-04-03 16:27:55 -04:00
if ! record . Internal {
2024-06-24 03:18:28 -04:00
err = externalDnsAdapter . DeleteDNSRecord ( recordId )
2024-04-03 16:27:55 -04:00
if err != nil {
log . Println ( err )
resp . WriteHeader ( http . StatusInternalServerError )
return failure ( context , req , resp )
}
}
err = database . DeleteDNSRecord ( context . DBConn , recordId )
2024-03-28 16:58:07 -04:00
if err != nil {
resp . WriteHeader ( http . StatusInternalServerError )
return failure ( context , req , resp )
}
2024-03-28 12:57:35 -04:00
2024-04-09 18:39:14 -04:00
formSuccess := types . BannerMessages {
Messages : [ ] string { "record deleted." } ,
}
( * context . TemplateData ) [ "Success" ] = formSuccess
2024-04-03 16:27:55 -04:00
return success ( context , req , resp )
2024-03-28 12:57:35 -04:00
}
2024-03-28 00:55:22 -04:00
}
}
2024-04-09 18:39:14 -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
}