Refactor the whole Go project for a better directory architecture
This commit is contained in:
parent
30aa02e6e2
commit
7b9544b3db
|
@ -12,7 +12,8 @@ import (
|
|||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.happydns.org/happydns/struct"
|
||||
"git.happydns.org/happydns/model"
|
||||
"git.happydns.org/happydns/storage"
|
||||
)
|
||||
|
||||
type Response interface {
|
||||
|
@ -58,8 +59,7 @@ func (r APIErrorResponse) WriteResponse(w http.ResponseWriter) {
|
|||
http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", r.err.Error()), r.status)
|
||||
}
|
||||
|
||||
|
||||
func apiHandler(f func(httprouter.Params, io.Reader) (Response)) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
func apiHandler(f func(httprouter.Params, io.Reader) Response) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
|
||||
r.RemoteAddr = addr
|
||||
|
@ -76,7 +76,7 @@ func apiHandler(f func(httprouter.Params, io.Reader) (Response)) func(http.Respo
|
|||
}
|
||||
}
|
||||
|
||||
func apiAuthHandler(f func(happydns.User, httprouter.Params, io.Reader) (Response)) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
func apiAuthHandler(f func(*happydns.User, httprouter.Params, io.Reader) Response) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
|
||||
r.RemoteAddr = addr
|
||||
|
@ -91,22 +91,22 @@ func apiAuthHandler(f func(happydns.User, httprouter.Params, io.Reader) (Respons
|
|||
|
||||
if flds := strings.Fields(r.Header.Get("Authorization")); len(flds) != 2 || flds[0] != "Bearer" {
|
||||
APIErrorResponse{
|
||||
err: errors.New("Authorization required"),
|
||||
err: errors.New("Authorization required"),
|
||||
status: http.StatusUnauthorized,
|
||||
}.WriteResponse(w)
|
||||
} else if sessionid, err := base64.StdEncoding.DecodeString(flds[1]); err != nil {
|
||||
APIErrorResponse{
|
||||
err: err,
|
||||
err: err,
|
||||
status: http.StatusUnauthorized,
|
||||
}.WriteResponse(w)
|
||||
} else if session, err := happydns.GetSession(sessionid); err != nil {
|
||||
} else if session, err := storage.MainStore.GetSession(sessionid); err != nil {
|
||||
APIErrorResponse{
|
||||
err: err,
|
||||
err: err,
|
||||
status: http.StatusUnauthorized,
|
||||
}.WriteResponse(w)
|
||||
} else if std, err := happydns.GetUser(int(session.IdUser)); err != nil {
|
||||
} else if std, err := storage.MainStore.GetUser(int(session.IdUser)); err != nil {
|
||||
APIErrorResponse{
|
||||
err: err,
|
||||
err: err,
|
||||
status: http.StatusUnauthorized,
|
||||
}.WriteResponse(w)
|
||||
} else {
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.happydns.org/happydns/struct"
|
||||
"git.happydns.org/happydns/model"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -8,19 +8,20 @@ import (
|
|||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.happydns.org/happydns/struct"
|
||||
"git.happydns.org/happydns/model"
|
||||
"git.happydns.org/happydns/storage"
|
||||
)
|
||||
|
||||
var AuthFunc = checkAuth
|
||||
|
||||
func init() {
|
||||
router.GET("/api/users/auth", apiAuthHandler(validateAuthToken))
|
||||
router.POST("/api/users/auth", apiHandler(func(ps httprouter.Params, b io.Reader) (Response) {
|
||||
router.POST("/api/users/auth", apiHandler(func(ps httprouter.Params, b io.Reader) Response {
|
||||
return AuthFunc(ps, b)
|
||||
}))
|
||||
}
|
||||
|
||||
func validateAuthToken(u happydns.User, _ httprouter.Params, _ io.Reader) (Response) {
|
||||
func validateAuthToken(u *happydns.User, _ httprouter.Params, _ io.Reader) Response {
|
||||
return APIResponse{
|
||||
response: u,
|
||||
}
|
||||
|
@ -39,18 +40,24 @@ func dummyAuth(_ httprouter.Params, body io.Reader) Response {
|
|||
}
|
||||
}
|
||||
|
||||
if user, err := happydns.GetUserByEmail(lf.Email); err != nil {
|
||||
if user, err := storage.MainStore.GetUserByEmail(lf.Email); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
} else {
|
||||
session, err := user.NewSession()
|
||||
session, err := happydns.NewSession(user)
|
||||
if err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
if err := storage.MainStore.CreateSession(session); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
res := map[string]interface{}{}
|
||||
res["status"] = "OK"
|
||||
res["id_session"] = session.Id
|
||||
|
@ -69,23 +76,29 @@ func checkAuth(_ httprouter.Params, body io.Reader) Response {
|
|||
}
|
||||
}
|
||||
|
||||
if user, err := happydns.GetUserByEmail(lf.Email); err != nil {
|
||||
if user, err := storage.MainStore.GetUserByEmail(lf.Email); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
} else if !user.CheckAuth(lf.Password) {
|
||||
return APIErrorResponse{
|
||||
err: errors.New(`{"status": "Invalid username or password"}`),
|
||||
err: errors.New(`{"status": "Invalid username or password"}`),
|
||||
status: http.StatusUnauthorized,
|
||||
}
|
||||
} else {
|
||||
session, err := user.NewSession()
|
||||
session, err := happydns.NewSession(user)
|
||||
if err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
if err := storage.MainStore.CreateSession(session); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
res := map[string]interface{}{}
|
||||
res["status"] = "OK"
|
||||
res["id_session"] = session.Id
|
||||
|
|
13
api/users.go
13
api/users.go
|
@ -7,7 +7,8 @@ import (
|
|||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.happydns.org/happydns/struct"
|
||||
"git.happydns.org/happydns/model"
|
||||
"git.happydns.org/happydns/storage"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -16,7 +17,7 @@ func init() {
|
|||
}
|
||||
|
||||
func listUsers(_ httprouter.Params, _ io.Reader) Response {
|
||||
if users, err := happydns.GetUsers(); err != nil {
|
||||
if users, err := storage.MainStore.GetUsers(); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
|
@ -28,8 +29,8 @@ func listUsers(_ httprouter.Params, _ io.Reader) Response {
|
|||
}
|
||||
|
||||
type UploadedUser struct {
|
||||
Email string
|
||||
Password string
|
||||
Email string
|
||||
Password string
|
||||
}
|
||||
|
||||
func registerUser(p httprouter.Params, body io.Reader) Response {
|
||||
|
@ -51,6 +52,10 @@ func registerUser(p httprouter.Params, body io.Reader) Response {
|
|||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
} else if err := storage.MainStore.CreateUser(user); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
} else {
|
||||
return APIResponse{
|
||||
response: user,
|
||||
|
|
37
api/zones.go
37
api/zones.go
|
@ -13,7 +13,8 @@ import (
|
|||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"git.happydns.org/happydns/struct"
|
||||
"git.happydns.org/happydns/model"
|
||||
"git.happydns.org/happydns/storage"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -26,8 +27,8 @@ func init() {
|
|||
router.DELETE("/api/zones/:zone/rr", apiAuthHandler(zoneHandler(delRR)))
|
||||
}
|
||||
|
||||
func getZones(u happydns.User, p httprouter.Params, body io.Reader) Response {
|
||||
if zones, err := u.GetZones(); err != nil {
|
||||
func getZones(u *happydns.User, p httprouter.Params, body io.Reader) Response {
|
||||
if zones, err := storage.MainStore.GetZones(u); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
|
@ -38,7 +39,7 @@ func getZones(u happydns.User, p httprouter.Params, body io.Reader) Response {
|
|||
}
|
||||
}
|
||||
|
||||
func addZone(u happydns.User, p httprouter.Params, body io.Reader) Response {
|
||||
func addZone(u *happydns.User, p httprouter.Params, body io.Reader) Response {
|
||||
var uz happydns.Zone
|
||||
err := json.NewDecoder(body).Decode(&uz)
|
||||
if err != nil {
|
||||
|
@ -61,23 +62,23 @@ func addZone(u happydns.User, p httprouter.Params, body io.Reader) Response {
|
|||
uz.KeyName = uz.KeyName + "."
|
||||
}
|
||||
|
||||
if happydns.ZoneExists(uz.DomainName) {
|
||||
if storage.MainStore.ZoneExists(uz.DomainName) {
|
||||
return APIErrorResponse{
|
||||
err: errors.New("This zone already exists."),
|
||||
}
|
||||
} else if zone, err := uz.NewZone(u); err != nil {
|
||||
} else if err := storage.MainStore.CreateZone(u, &uz); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
} else {
|
||||
return APIResponse{
|
||||
response: zone,
|
||||
response: uz,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func delZone(zone happydns.Zone, body io.Reader) Response {
|
||||
if _, err := zone.Delete(); err != nil {
|
||||
func delZone(zone *happydns.Zone, body io.Reader) Response {
|
||||
if err := storage.MainStore.DeleteZone(zone); err != nil {
|
||||
return APIErrorResponse{
|
||||
err: err,
|
||||
}
|
||||
|
@ -88,9 +89,9 @@ func delZone(zone happydns.Zone, body io.Reader) Response {
|
|||
}
|
||||
}
|
||||
|
||||
func zoneHandler(f func(happydns.Zone, io.Reader) Response) func(happydns.User, httprouter.Params, io.Reader) Response {
|
||||
return func(u happydns.User, ps httprouter.Params, body io.Reader) Response {
|
||||
if zone, err := u.GetZoneByDN(ps.ByName("zone")); err != nil {
|
||||
func zoneHandler(f func(*happydns.Zone, io.Reader) Response) func(*happydns.User, httprouter.Params, io.Reader) Response {
|
||||
return func(u *happydns.User, ps httprouter.Params, body io.Reader) Response {
|
||||
if zone, err := storage.MainStore.GetZoneByDN(u, ps.ByName("zone")); err != nil {
|
||||
return APIErrorResponse{
|
||||
status: http.StatusNotFound,
|
||||
err: errors.New("Domain not found"),
|
||||
|
@ -101,7 +102,7 @@ func zoneHandler(f func(happydns.Zone, io.Reader) Response) func(happydns.User,
|
|||
}
|
||||
}
|
||||
|
||||
func getZone(zone happydns.Zone, body io.Reader) Response {
|
||||
func getZone(zone *happydns.Zone, body io.Reader) Response {
|
||||
return APIResponse{
|
||||
response: zone,
|
||||
}
|
||||
|
@ -117,13 +118,13 @@ func normalizeNSServer(srv string) string {
|
|||
}
|
||||
}
|
||||
|
||||
func axfrZone(zone happydns.Zone, body io.Reader) Response {
|
||||
func axfrZone(zone *happydns.Zone, body io.Reader) Response {
|
||||
d := net.Dialer{}
|
||||
con, err := d.Dial("tcp", normalizeNSServer(zone.Server))
|
||||
if err != nil {
|
||||
return APIErrorResponse{
|
||||
status: http.StatusInternalServerError,
|
||||
err: err,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
defer con.Close()
|
||||
|
@ -133,7 +134,7 @@ func axfrZone(zone happydns.Zone, body io.Reader) Response {
|
|||
m.SetAxfr(zone.DomainName)
|
||||
m.SetTsig(zone.KeyName, zone.KeyAlgo, 300, time.Now().Unix())
|
||||
|
||||
dnscon := &dns.Conn{Conn:con}
|
||||
dnscon := &dns.Conn{Conn: con}
|
||||
transfer := &dns.Transfer{Conn: dnscon, TsigSecret: map[string]string{zone.KeyName: zone.Base64KeyBlob()}}
|
||||
c, err := transfer.In(m, normalizeNSServer(zone.Server))
|
||||
|
||||
|
@ -172,7 +173,7 @@ type uploadedRR struct {
|
|||
RR string `json:"string"`
|
||||
}
|
||||
|
||||
func addRR(zone happydns.Zone, body io.Reader) Response {
|
||||
func addRR(zone *happydns.Zone, body io.Reader) Response {
|
||||
var urr uploadedRR
|
||||
err := json.NewDecoder(body).Decode(&urr)
|
||||
if err != nil {
|
||||
|
@ -217,7 +218,7 @@ func addRR(zone happydns.Zone, body io.Reader) Response {
|
|||
}
|
||||
}
|
||||
|
||||
func delRR(zone happydns.Zone, body io.Reader) Response {
|
||||
func delRR(zone *happydns.Zone, body io.Reader) Response {
|
||||
var urr uploadedRR
|
||||
err := json.NewDecoder(body).Decode(&urr)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package main
|
||||
|
||||
//go:generate go run generators/gen_database_migration.go
|
|
@ -0,0 +1,68 @@
|
|||
package main
|
||||
|
||||
// +build ignore
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const tpl = `// Code generated by go generate. DO NOT EDIT.
|
||||
|
||||
package database // import "happydns.org/database"
|
||||
|
||||
const schemaVersion = {{ .MaxVersion }}
|
||||
|
||||
var schemaRevisions = map[uint16]string{
|
||||
{{ range $constant, $content := .Map }}` + "\t" + `{{ $constant }}: ` + "`{{ $content }}`" + `,
|
||||
{{ end }}}
|
||||
`
|
||||
|
||||
var bundleTpl = template.Must(template.New("").Parse(tpl))
|
||||
|
||||
type valTpl struct {
|
||||
MaxVersion uint64
|
||||
Map map[uint16]string
|
||||
}
|
||||
|
||||
func main() {
|
||||
srcFiles, err := filepath.Glob("storage/mysql/schemas/*.sql")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
d := valTpl{
|
||||
MaxVersion: 0,
|
||||
Map: map[uint16]string{},
|
||||
}
|
||||
|
||||
for _, srcFile := range srcFiles {
|
||||
data, err := ioutil.ReadFile(srcFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
v, err := strconv.ParseUint(strings.TrimSuffix(path.Base(srcFile), ".sql"), 10, 16)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
d.Map[uint16(v)] = string(data)
|
||||
if v > d.MaxVersion {
|
||||
d.MaxVersion = v
|
||||
}
|
||||
}
|
||||
|
||||
f, err := os.Create("storage/mysql/schemas.go")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
bundleTpl.Execute(f, d)
|
||||
}
|
1
go.sum
1
go.sum
|
@ -18,5 +18,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
17
main.go
17
main.go
|
@ -9,7 +9,8 @@ import (
|
|||
"strings"
|
||||
|
||||
"git.happydns.org/happydns/api"
|
||||
"git.happydns.org/happydns/struct"
|
||||
"git.happydns.org/happydns/storage"
|
||||
"git.happydns.org/happydns/storage/mysql"
|
||||
)
|
||||
|
||||
type ResponseWriterPrefix struct {
|
||||
|
@ -56,7 +57,7 @@ func main() {
|
|||
// Read parameters from command line
|
||||
flag.StringVar(&DevProxy, "dev", DevProxy, "Proxify traffic to this host for static assets")
|
||||
var bind = flag.String("bind", ":8081", "Bind port/socket")
|
||||
var dsn = flag.String("dsn", happydns.DSNGenerator(), "DSN to connect to the MySQL server")
|
||||
var dsn = flag.String("dsn", database.DSNGenerator(), "DSN to connect to the MySQL server")
|
||||
var baseURL = flag.String("baseurl", "/", "URL prepended to each URL")
|
||||
flag.StringVar(&api.DefaultNameServer, "defaultns", api.DefaultNameServer, "Adress to the default name server")
|
||||
flag.Parse()
|
||||
|
@ -72,14 +73,16 @@ func main() {
|
|||
|
||||
// Initialize contents
|
||||
log.Println("Opening database...")
|
||||
if err := happydns.DBInit(*dsn); err != nil {
|
||||
if store, err := database.NewMySQLStorage(*dsn); err != nil {
|
||||
log.Fatal("Cannot open the database: ", err)
|
||||
} else {
|
||||
storage.MainStore = store
|
||||
}
|
||||
defer happydns.DBClose()
|
||||
defer storage.MainStore.Close()
|
||||
|
||||
log.Println("Creating database...")
|
||||
if err := happydns.DBCreate(); err != nil {
|
||||
log.Fatal("Cannot create database: ", err)
|
||||
log.Println("Do database migrations...")
|
||||
if err := storage.MainStore.DoMigration(); err != nil {
|
||||
log.Fatal("Cannot migrate database: ", err)
|
||||
}
|
||||
|
||||
// Serve content
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package happydns
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
Id []byte `json:"id"`
|
||||
IdUser int64 `json:"login"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
func NewSession(user *User) (s *Session, err error) {
|
||||
session_id := make([]byte, 255)
|
||||
_, err = rand.Read(session_id)
|
||||
if err == nil {
|
||||
s = &Session{
|
||||
Id: session_id,
|
||||
IdUser: user.Id,
|
||||
Time: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package happydns
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Password []byte
|
||||
Salt []byte
|
||||
RegistrationTime *time.Time `json:"registration_time"`
|
||||
}
|
||||
|
||||
type Users []*User
|
||||
|
||||
func GenPassword(password string, salt []byte) []byte {
|
||||
return hmac.New(sha512.New512_224, []byte(password)).Sum([]byte(salt))
|
||||
}
|
||||
|
||||
func NewUser(email string, password string) (*User, error) {
|
||||
salt := make([]byte, 64)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashedpass := GenPassword(password, salt)
|
||||
t := time.Now()
|
||||
return &User{
|
||||
Id: 0,
|
||||
Email: email,
|
||||
Password: hashedpass,
|
||||
Salt: salt,
|
||||
RegistrationTime: &t,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *User) CheckAuth(password string) bool {
|
||||
pass := GenPassword(password, u.Salt)
|
||||
if len(pass) != len(u.Password) {
|
||||
return false
|
||||
} else {
|
||||
for k := range pass {
|
||||
if pass[k] != u.Password[k] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package happydns
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
type Zone struct {
|
||||
Id int64 `json:"id"`
|
||||
IdUser int64
|
||||
DomainName string `json:"domain"`
|
||||
Server string `json:"server,omitempty"`
|
||||
KeyName string `json:"keyname,omitempty"`
|
||||
KeyAlgo string `json:"algorithm,omitempty"`
|
||||
KeyBlob []byte `json:"keyblob,omitempty"`
|
||||
StorageFacility string `json:"storage_facility,omitempty"`
|
||||
}
|
||||
|
||||
type Zones []*Zone
|
||||
|
||||
func NewZone(u User, dn, server, keyname, algo string, keyblob []byte, storage string) *Zone {
|
||||
return &Zone{
|
||||
IdUser: u.Id,
|
||||
DomainName: dn,
|
||||
Server: server,
|
||||
KeyName: keyname,
|
||||
KeyAlgo: algo,
|
||||
KeyBlob: keyblob,
|
||||
StorageFacility: storage,
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Zone) Base64KeyBlob() string {
|
||||
return base64.StdEncoding.EncodeToString(z.KeyBlob)
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package storage // import "happydns.org/storage"
|
||||
|
||||
import (
|
||||
"git.happydns.org/happydns/model"
|
||||
)
|
||||
|
||||
type Storage interface {
|
||||
DoMigration() error
|
||||
Close() error
|
||||
|
||||
GetSession(id []byte) (*happydns.Session, error)
|
||||
CreateSession(session *happydns.Session) error
|
||||
UpdateSession(session *happydns.Session) error
|
||||
DeleteSession(session *happydns.Session) error
|
||||
ClearSession() error
|
||||
|
||||
GetUsers() (happydns.Users, error)
|
||||
GetUser(id int) (*happydns.User, error)
|
||||
GetUserByEmail(email string) (*happydns.User, error)
|
||||
UserExists(email string) bool
|
||||
CreateUser(user *happydns.User) error
|
||||
UpdateUser(user *happydns.User) error
|
||||
DeleteUser(user *happydns.User) error
|
||||
ClearUsers() error
|
||||
|
||||
GetZones(u *happydns.User) (happydns.Zones, error)
|
||||
GetZone(u *happydns.User, id int) (*happydns.Zone, error)
|
||||
GetZoneByDN(u *happydns.User, dn string) (*happydns.Zone, error)
|
||||
ZoneExists(dn string) bool
|
||||
CreateZone(u *happydns.User, z *happydns.Zone) error
|
||||
UpdateZone(z *happydns.Zone) error
|
||||
UpdateZoneOwner(z *happydns.Zone, newOwner *happydns.User) error
|
||||
DeleteZone(z *happydns.Zone) error
|
||||
ClearZones() error
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package database // import "happydns.org/database"
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
type MySQLStorage struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewMySQLStorage establishes the connection to the database
|
||||
func NewMySQLStorage(dsn string) (*MySQLStorage, error) {
|
||||
if db, err := sql.Open("mysql", dsn+"?parseTime=true&foreign_key_checks=1"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
_, err := db.Exec(`SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO';`)
|
||||
for i := 0; err != nil && i < 45; i += 1 {
|
||||
if _, err = db.Exec(`SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO';`); err != nil && i <= 45 {
|
||||
log.Println("An error occurs when trying to connect to DB, will retry in 2 seconds: ", err)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MySQLStorage{db}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) DoMigration() error {
|
||||
var currentVersion uint16
|
||||
s.db.QueryRow(`SELECT version FROM schema_version`).Scan(¤tVersion)
|
||||
|
||||
log.Println("Current schema version:", currentVersion)
|
||||
log.Println("Latest schema version:", schemaVersion)
|
||||
|
||||
for version := currentVersion + 1; version <= schemaVersion; version++ {
|
||||
log.Println("Migrating to version:", version)
|
||||
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawSQL := schemaRevisions[version]
|
||||
for _, request := range strings.Split(rawSQL, ";") {
|
||||
if len(strings.TrimSpace(request)) == 0 {
|
||||
continue
|
||||
}
|
||||
_, err = tx.Exec(request)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`delete from schema_version`); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`INSERT INTO schema_version (version) VALUES (?)`, version); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) Close() error {
|
||||
return s.db.Close()
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// DSNGenerator returns DSN filed with values from environment
|
||||
func DSNGenerator() string {
|
||||
db_user := "happydns"
|
||||
db_password := "happydns"
|
||||
db_host := ""
|
||||
db_db := "happydns"
|
||||
|
||||
if v, exists := os.LookupEnv("MYSQL_HOST"); exists {
|
||||
db_host = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("MYSQL_PASSWORD"); exists {
|
||||
db_password = v
|
||||
} else if v, exists := os.LookupEnv("MYSQL_ROOT_PASSWORD"); exists {
|
||||
db_user = "root"
|
||||
db_password = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("MYSQL_USER"); exists {
|
||||
db_user = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("MYSQL_DATABASE"); exists {
|
||||
db_db = v
|
||||
}
|
||||
|
||||
return db_user + ":" + db_password + "@" + db_host + "/" + db_db
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// Code generated by go generate. DO NOT EDIT.
|
||||
|
||||
package database // import "happydns.org/database"
|
||||
|
||||
const schemaVersion = 1
|
||||
|
||||
var schemaRevisions = map[uint16]string{
|
||||
1: `CREATE TABLE schema_version (
|
||||
version SMALLINT UNSIGNED NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE users (
|
||||
id_user INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password BINARY(92) NOT NULL,
|
||||
salt BINARY(64) NOT NULL,
|
||||
registration_time TIMESTAMP NOT NULL
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE user_sessions (
|
||||
id_session BLOB(255) NOT NULL,
|
||||
id_user INTEGER NOT NULL,
|
||||
time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY(id_user) REFERENCES users(id_user)
|
||||
);
|
||||
|
||||
CREATE TABLE zones (
|
||||
id_zone INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
id_user INTEGER NOT NULL,
|
||||
domain VARCHAR(255) NOT NULL,
|
||||
server VARCHAR(255),
|
||||
key_name VARCHAR(255) NOT NULL,
|
||||
key_algo ENUM("hmac-md5.sig-alg.reg.int.", "hmac-sha1.", "hmac-sha224.", "hmac-sha256.", "hmac-sha384.", "hmac-sha512.") NOT NULL DEFAULT "hmac-sha256.",
|
||||
key_blob BLOB NOT NULL,
|
||||
storage_facility ENUM("live", "history") NOT NULL DEFAULT "live",
|
||||
FOREIGN KEY(id_user) REFERENCES users(id_user)
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
`,
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
CREATE TABLE schema_version (
|
||||
version SMALLINT UNSIGNED NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE users (
|
||||
id_user INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password BINARY(92) NOT NULL,
|
||||
salt BINARY(64) NOT NULL,
|
||||
registration_time TIMESTAMP NOT NULL
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE user_sessions (
|
||||
id_session BLOB(255) NOT NULL,
|
||||
id_user INTEGER NOT NULL,
|
||||
time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY(id_user) REFERENCES users(id_user)
|
||||
);
|
||||
|
||||
CREATE TABLE zones (
|
||||
id_zone INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
id_user INTEGER NOT NULL,
|
||||
domain VARCHAR(255) NOT NULL,
|
||||
server VARCHAR(255),
|
||||
key_name VARCHAR(255) NOT NULL,
|
||||
key_algo ENUM("hmac-md5.sig-alg.reg.int.", "hmac-sha1.", "hmac-sha224.", "hmac-sha256.", "hmac-sha384.", "hmac-sha512.") NOT NULL DEFAULT "hmac-sha256.",
|
||||
key_blob BLOB NOT NULL,
|
||||
storage_facility ENUM("live", "history") NOT NULL DEFAULT "live",
|
||||
FOREIGN KEY(id_user) REFERENCES users(id_user)
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
|
@ -0,0 +1,40 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
"git.happydns.org/happydns/model"
|
||||
)
|
||||
|
||||
func (s *MySQLStorage) GetSession(id []byte) (session *happydns.Session, err error) {
|
||||
session = &happydns.Session{}
|
||||
err = s.db.QueryRow("SELECT id_session, id_user, time FROM user_sessions WHERE id_session=?", id).Scan(&session.Id, &session.IdUser, &session.Time)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) CreateSession(session *happydns.Session) error {
|
||||
session_id := make([]byte, 255)
|
||||
if _, err := rand.Read(session_id); err != nil {
|
||||
return err
|
||||
} else if _, err := s.db.Exec("INSERT INTO user_sessions (id_session, id_user, time) VALUES (?, ?, ?)", session_id, session.IdUser, session.Time); err != nil {
|
||||
return err
|
||||
} else {
|
||||
session.Id = session_id
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) UpdateSession(session *happydns.Session) error {
|
||||
_, err := s.db.Exec(`UPDATE user_sessions SET id_user = ?, time = ? WHERE id_session = ?`, session.IdUser, session.Time, session.Id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) DeleteSession(session *happydns.Session) error {
|
||||
_, err := s.db.Exec("DELETE FROM user_sessions WHERE id_session = ?", session.Id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) ClearSession() error {
|
||||
_, err := s.db.Exec("DELETE FROM user_sessions")
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"git.happydns.org/happydns/model"
|
||||
)
|
||||
|
||||
func (s *MySQLStorage) GetUsers() (users happydns.Users, err error) {
|
||||
if rows, errr := s.db.Query("SELECT id_user, email, password, salt, registration_time FROM users"); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var u happydns.User
|
||||
if err = rows.Scan(&u.Id, &u.Email, &u.Password, &u.Salt, &u.RegistrationTime); err != nil {
|
||||
return
|
||||
}
|
||||
users = append(users, &u)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) GetUser(id int) (u *happydns.User, err error) {
|
||||
u = &happydns.User{}
|
||||
err = s.db.QueryRow("SELECT id_user, email, password, salt, registration_time FROM users WHERE id_user=?", id).Scan(&u.Id, &u.Email, &u.Password, &u.Salt, &u.RegistrationTime)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) GetUserByEmail(email string) (u *happydns.User, err error) {
|
||||
u = &happydns.User{}
|
||||
err = s.db.QueryRow("SELECT id_user, email, password, salt, registration_time FROM users WHERE email=?", email).Scan(&u.Id, &u.Email, &u.Password, &u.Salt, &u.RegistrationTime)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) UserExists(email string) bool {
|
||||
var z int
|
||||
err := s.db.QueryRow("SELECT 1 FROM users WHERE email=?", email).Scan(&z)
|
||||
return err == nil && z == 1
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) CreateUser(u *happydns.User) error {
|
||||
if res, err := s.db.Exec("INSERT INTO users (email, password, salt, registration_time) VALUES (?, ?, ?, ?)", u.Email, u.Password, u.Salt, u.RegistrationTime); err != nil {
|
||||
return err
|
||||
} else if sid, err := res.LastInsertId(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
u.Id = sid
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) UpdateUser(u *happydns.User) error {
|
||||
_, err := s.db.Exec("UPDATE users SET email = ?, password = ?, salt = ?, registration_time = ? WHERE id_user = ?", u.Email, u.Password, u.Salt, u.RegistrationTime, u.Id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) DeleteUser(u *happydns.User) error {
|
||||
_, err := s.db.Exec("DELETE FROM users WHERE id_user = ?", u.Id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) ClearUsers() error {
|
||||
_, err := s.db.Exec("DELETE FROM users")
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"git.happydns.org/happydns/model"
|
||||
)
|
||||
|
||||
func (s *MySQLStorage) GetZones(u *happydns.User) (zones happydns.Zones, err error) {
|
||||
if rows, errr := s.db.Query("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones WHERE id_user = ?", u.Id); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var z happydns.Zone
|
||||
if err = rows.Scan(&z.Id, &z.IdUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility); err != nil {
|
||||
return
|
||||
}
|
||||
zones = append(zones, &z)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) GetZone(u *happydns.User, id int) (z *happydns.Zone, err error) {
|
||||
z = &happydns.Zone{}
|
||||
err = s.db.QueryRow("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones WHERE id_zone=? AND id_user=?", id, u.Id).Scan(&z.Id, &z.IdUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) GetZoneByDN(u *happydns.User, dn string) (z *happydns.Zone, err error) {
|
||||
z = &happydns.Zone{}
|
||||
err = s.db.QueryRow("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones WHERE domain=? AND id_user=?", dn, u.Id).Scan(&z.Id, &z.IdUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) ZoneExists(dn string) bool {
|
||||
var z int
|
||||
err := s.db.QueryRow("SELECT 1 FROM zones WHERE domain=?", dn).Scan(&z)
|
||||
return err == nil && z == 1
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) CreateZone(u *happydns.User, z *happydns.Zone) error {
|
||||
if res, err := s.db.Exec("INSERT INTO zones (id_user, domain, server, key_name, key_blob, storage_facility) VALUES (?, ?, ?, ?, ?, ?)", u.Id, z.DomainName, z.Server, z.KeyName, z.KeyBlob, z.StorageFacility); err != nil {
|
||||
return err
|
||||
} else if z.Id, err = res.LastInsertId(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) UpdateZone(z *happydns.Zone) error {
|
||||
if _, err := s.db.Exec("UPDATE zones SET domain = ?, key_name = ?, key_algo = ?, key_blob = ?, storage_facility = ? WHERE id_zone = ?", z.DomainName, z.KeyName, z.KeyAlgo, z.KeyBlob, z.StorageFacility, z.Id); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) UpdateZoneOwner(z *happydns.Zone, newOwner *happydns.User) error {
|
||||
if _, err := s.db.Exec("UPDATE zones SET id_user = ? WHERE id_zone = ?", newOwner.Id, z.Id); err != nil {
|
||||
return err
|
||||
} else {
|
||||
z.IdUser = newOwner.Id
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) DeleteZone(z *happydns.Zone) error {
|
||||
_, err := s.db.Exec("DELETE FROM zones WHERE id_zone = ?", z.Id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *MySQLStorage) ClearZones() error {
|
||||
_, err := s.db.Exec("DELETE FROM zones")
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package storage
|
||||
|
||||
var MainStore Storage
|
117
struct/db.go
117
struct/db.go
|
@ -1,117 +0,0 @@
|
|||
package happydns
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// db stores the connection to the database
|
||||
var db *sql.DB
|
||||
|
||||
// DSNGenerator returns DSN filed with values from environment
|
||||
func DSNGenerator() string {
|
||||
db_user := "happydns"
|
||||
db_password := "happydns"
|
||||
db_host := ""
|
||||
db_db := "happydns"
|
||||
|
||||
if v, exists := os.LookupEnv("MYSQL_HOST"); exists {
|
||||
db_host = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("MYSQL_PASSWORD"); exists {
|
||||
db_password = v
|
||||
} else if v, exists := os.LookupEnv("MYSQL_ROOT_PASSWORD"); exists {
|
||||
db_user = "root"
|
||||
db_password = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("MYSQL_USER"); exists {
|
||||
db_user = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("MYSQL_DATABASE"); exists {
|
||||
db_db = v
|
||||
}
|
||||
|
||||
return db_user + ":" + db_password + "@" + db_host + "/" + db_db
|
||||
}
|
||||
|
||||
// DBInit establishes the connection to the database
|
||||
func DBInit(dsn string) (err error) {
|
||||
if db, err = sql.Open("mysql", dsn+"?parseTime=true&foreign_key_checks=1"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = db.Exec(`SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO';`)
|
||||
for i := 0; err != nil && i < 45; i += 1 {
|
||||
if _, err = db.Exec(`SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO';`); err != nil && i <= 45 {
|
||||
log.Println("An error occurs when trying to connect to DB, will retry in 2 seconds: ", err)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DBCreate creates all necessary tables used by the package
|
||||
func DBCreate() error {
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS users(
|
||||
id_user INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password BINARY(92) NOT NULL,
|
||||
salt BINARY(64) NOT NULL,
|
||||
registration_time TIMESTAMP NOT NULL
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS user_sessions(
|
||||
id_session BLOB(255) NOT NULL,
|
||||
id_user INTEGER NOT NULL,
|
||||
time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY(id_user) REFERENCES users(id_user)
|
||||
);
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS zones(
|
||||
id_zone INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
id_user INTEGER NOT NULL,
|
||||
domain VARCHAR(255) NOT NULL,
|
||||
server VARCHAR(255),
|
||||
key_name VARCHAR(255) NOT NULL,
|
||||
key_algo ENUM("hmac-md5.sig-alg.reg.int.", "hmac-sha1.", "hmac-sha224.", "hmac-sha256.", "hmac-sha384.", "hmac-sha512.") NOT NULL DEFAULT "hmac-sha256.",
|
||||
key_blob BLOB NOT NULL,
|
||||
storage_facility ENUM("live", "history") NOT NULL DEFAULT "live",
|
||||
FOREIGN KEY(id_user) REFERENCES users(id_user)
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DBClose closes the connection to the database
|
||||
func DBClose() error {
|
||||
return db.Close()
|
||||
}
|
||||
|
||||
func DBPrepare(query string) (*sql.Stmt, error) {
|
||||
return db.Prepare(query)
|
||||
}
|
||||
|
||||
func DBQuery(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
return db.Query(query, args...)
|
||||
}
|
||||
|
||||
func DBExec(query string, args ...interface{}) (sql.Result, error) {
|
||||
return db.Exec(query, args...)
|
||||
}
|
||||
|
||||
func DBQueryRow(query string, args ...interface{}) *sql.Row {
|
||||
return db.QueryRow(query, args...)
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package happydns
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
Id []byte `json:"id"`
|
||||
IdUser int64 `json:"login"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
func GetSession(id []byte) (s Session, err error) {
|
||||
err = DBQueryRow("SELECT id_session, id_user, time FROM user_sessions WHERE id_session=?", id).Scan(&s.Id, &s.IdUser, &s.Time)
|
||||
return
|
||||
}
|
||||
|
||||
func (user User) NewSession() (Session, error) {
|
||||
session_id := make([]byte, 255)
|
||||
if _, err := rand.Read(session_id); err != nil {
|
||||
return Session{}, err
|
||||
} else if _, err := DBExec("INSERT INTO user_sessions (id_session, id_user, time) VALUES (?, ?, ?)", session_id, user.Id, time.Now()); err != nil {
|
||||
return Session{}, err
|
||||
} else {
|
||||
return Session{session_id, user.Id, time.Now()}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s Session) Update() (int64, error) {
|
||||
if res, err := DBExec("UPDATE user_sessions SET id_user = ?, time = ? WHERE id_session = ?", s.IdUser, s.Time, s.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func (s Session) Delete() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM user_sessions WHERE id_session = ?", s.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func ClearSession() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM user_sessions"); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
117
struct/user.go
117
struct/user.go
|
@ -1,117 +0,0 @@
|
|||
package happydns
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
password []byte
|
||||
salt []byte
|
||||
RegistrationTime *time.Time `json:"registration_time"`
|
||||
}
|
||||
|
||||
func GetUsers() (users []User, err error) {
|
||||
if rows, errr := DBQuery("SELECT id_user, email, password, salt, registration_time FROM users"); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var u User
|
||||
if err = rows.Scan(&u.Id, &u.Email, &u.password, &u.salt, &u.RegistrationTime); err != nil {
|
||||
return
|
||||
}
|
||||
users = append(users, u)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func GetUser(id int) (u User, err error) {
|
||||
err = DBQueryRow("SELECT id_user, email, password, salt, registration_time FROM users WHERE id_user=?", id).Scan(&u.Id, &u.Email, &u.password, &u.salt, &u.RegistrationTime)
|
||||
return
|
||||
}
|
||||
|
||||
func GetUserByEmail(email string) (u User, err error) {
|
||||
err = DBQueryRow("SELECT id_user, email, password, salt, registration_time FROM users WHERE email=?", email).Scan(&u.Id, &u.Email, &u.password, &u.salt, &u.RegistrationTime)
|
||||
return
|
||||
}
|
||||
|
||||
func UserExists(email string) bool {
|
||||
var z int
|
||||
err := DBQueryRow("SELECT 1 FROM users WHERE email=?", email).Scan(&z)
|
||||
return err == nil && z == 1
|
||||
}
|
||||
|
||||
func GenPassword(password string, salt []byte) []byte {
|
||||
return hmac.New(sha512.New512_224, []byte(password)).Sum([]byte(salt))
|
||||
}
|
||||
|
||||
func NewUser(email string, password string) (User, error) {
|
||||
salt := make([]byte, 64)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
t := time.Now()
|
||||
hashedpass := GenPassword(password, salt)
|
||||
if res, err := DBExec("INSERT INTO users (email, password, salt, registration_time) VALUES (?, ?, ?, ?)", email, hashedpass, salt, t); err != nil {
|
||||
return User{}, err
|
||||
} else if sid, err := res.LastInsertId(); err != nil {
|
||||
return User{}, err
|
||||
} else {
|
||||
return User{sid, email, hashedpass, salt, &t}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) CheckAuth(password string) bool {
|
||||
pass := GenPassword(password, u.salt)
|
||||
if len(pass) != len(u.password) {
|
||||
return false
|
||||
} else {
|
||||
for k := range pass {
|
||||
if pass[k] != u.password[k] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) Update() (int64, error) {
|
||||
if res, err := DBExec("UPDATE users SET email = ?, password = ?, salt = ?, registration_time = ? WHERE id_user = ?", u.Email, u.password, u.salt, u.RegistrationTime, u.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) Delete() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM users WHERE id_user = ?", u.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func ClearUsers() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM users"); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
139
struct/zone.go
139
struct/zone.go
|
@ -1,139 +0,0 @@
|
|||
package happydns
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
type Zone struct {
|
||||
Id int64 `json:"id"`
|
||||
idUser int64
|
||||
DomainName string `json:"domain"`
|
||||
Server string `json:"server,omitempty"`
|
||||
KeyName string `json:"keyname,omitempty"`
|
||||
KeyAlgo string `json:"algorithm,omitempty"`
|
||||
KeyBlob []byte `json:"keyblob,omitempty"`
|
||||
StorageFacility string `json:"storage_facility,omitempty"`
|
||||
}
|
||||
|
||||
func GetZones() (zones []Zone, err error) {
|
||||
if rows, errr := DBQuery("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones"); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var z Zone
|
||||
if err = rows.Scan(&z.Id, &z.idUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility); err != nil {
|
||||
return
|
||||
}
|
||||
zones = append(zones, z)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) GetZones() (zones []Zone, err error) {
|
||||
if rows, errr := DBQuery("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones WHERE id_user = ?", u.Id); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var z Zone
|
||||
if err = rows.Scan(&z.Id, &z.idUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility); err != nil {
|
||||
return
|
||||
}
|
||||
zones = append(zones, z)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func GetZone(id int) (z Zone, err error) {
|
||||
err = DBQueryRow("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones WHERE id_zone=?", id).Scan(&z.Id, &z.idUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility)
|
||||
return
|
||||
}
|
||||
|
||||
func (u *User) GetZone(id int) (z Zone, err error) {
|
||||
err = DBQueryRow("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones WHERE id_zone=? AND id_user=?", id, u.Id).Scan(&z.Id, &z.idUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility)
|
||||
return
|
||||
}
|
||||
|
||||
func GetZoneByDN(dn string) (z Zone, err error) {
|
||||
err = DBQueryRow("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones WHERE domain=?", dn).Scan(&z.Id, &z.idUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility)
|
||||
return
|
||||
}
|
||||
|
||||
func (u *User) GetZoneByDN(dn string) (z Zone, err error) {
|
||||
err = DBQueryRow("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones WHERE domain=? AND id_user=?", dn, u.Id).Scan(&z.Id, &z.idUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility)
|
||||
return
|
||||
}
|
||||
|
||||
func ZoneExists(dn string) bool {
|
||||
var z int
|
||||
err := DBQueryRow("SELECT 1 FROM zones WHERE domain=?", dn).Scan(&z)
|
||||
return err == nil && z == 1
|
||||
}
|
||||
|
||||
func (z *Zone) NewZone(u User) (Zone, error) {
|
||||
if res, err := DBExec("INSERT INTO zones (id_user, domain, server, key_name, key_blob, storage_facility) VALUES (?, ?, ?, ?, ?, ?)", u.Id, z.DomainName, z.Server, z.KeyName, z.KeyBlob, z.StorageFacility); err != nil {
|
||||
return *z, err
|
||||
} else if z.Id, err = res.LastInsertId(); err != nil {
|
||||
return *z, err
|
||||
} else {
|
||||
return *z, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Zone) Update() (int64, error) {
|
||||
if res, err := DBExec("UPDATE zones SET domain = ?, key_name = ?, key_algo = ?, key_blob = ?, storage_facility = ? WHERE id_zone = ?", z.DomainName, z.KeyName, z.KeyAlgo, z.KeyBlob, z.StorageFacility, z.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Zone) UpdateOwner(u User) (int64, error) {
|
||||
if res, err := DBExec("UPDATE zones SET id_user = ? WHERE id_zone = ?", u.Id, z.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
z.idUser = u.Id
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Zone) Delete() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM zones WHERE id_zone = ?", z.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Zone) Base64KeyBlob() string {
|
||||
return base64.StdEncoding.EncodeToString(z.KeyBlob)
|
||||
}
|
||||
|
||||
func ClearZones() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM zones"); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue