sum7/warehost
sum7
/
warehost
Archived
1
0
Fork 0

add warehost-host and working on warehost-ftp

This commit is contained in:
Martin Geno 2016-11-27 10:46:50 +01:00
parent e0c93cc4a5
commit 67ee4a8559
12 changed files with 727 additions and 19 deletions

View File

@ -2,7 +2,6 @@ package main
import ( import (
"io/ioutil" "io/ioutil"
"log"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -18,7 +17,7 @@ func ReadConfigFile(path string) *Config {
file, _ := ioutil.ReadFile(path) file, _ := ioutil.ReadFile(path)
err := yaml.Unmarshal(file, &config) err := yaml.Unmarshal(file, &config)
if err != nil { if err != nil {
log.Fatal(err) panic(err)
} }
return config return config
} }

View File

@ -1,9 +1,8 @@
package main package main
import ( import (
"bufio" "flag"
"os" "os"
"os/exec"
"database/sql" "database/sql"
_ "github.com/lib/pq" _ "github.com/lib/pq"
@ -15,41 +14,33 @@ var (
configFile string configFile string
username string username string
password string password string
execCmd string
config *Config config *Config
db *sql.DB db *sql.DB
err error err error
) )
func main() { func main() {
configFile = os.Args[1] flag.StringVar(&configFile, "c", "config.yml", "path of configuration file")
execCmd = os.Args[2] flag.StringVar(&username, "u", "none", "username")
flag.StringVar(&password, "p", "none", "password")
pipe := os.NewFile(uintptr(3), "/dev/fd/3") flag.Parse()
defer pipe.Close()
in := bufio.NewReader(pipe)
data, _ := in.ReadBytes(0)
username = string(data[:len(data)-1])
data, _ = in.ReadBytes(0)
password = string(data[:len(data)-1])
config = ReadConfigFile(configFile) config = ReadConfigFile(configFile)
db, err = sql.Open("postgres", config.Database) db, err = sql.Open("postgres", config.Database)
if err != nil { if err != nil {
os.Exit(1) os.Exit(111)
} }
defer db.Close() defer db.Close()
var realPassword string var realPassword string
err = db.QueryRow("select password from login where mail = $1", username).Scan(&realPassword) err = db.QueryRow("select password from login where mail = $1", username).Scan(&realPassword)
if err != nil { if err != nil {
os.Exit(1) os.Exit(3)
} }
output, _ := libpassword.Validate(realPassword, password) output, _ := libpassword.Validate(realPassword, password)
if output { if output {
exec.Command("bash", "-c", execCmd).Run() os.Exit(0)
} else { } else {
os.Exit(1) os.Exit(1)
} }

35
cmd/warehost-ftp/auth.go Normal file
View File

@ -0,0 +1,35 @@
package main
import (
"github.com/jinzhu/gorm"
liblog "dev.sum7.eu/sum7/warehost/lib/log"
libpassword "dev.sum7.eu/sum7/warehost/lib/password"
system "dev.sum7.eu/sum7/warehost/system"
)
type WarehostAuth struct {
db *gorm.DB
}
func (this WarehostAuth) CheckPasswd(user, pass string) (returndata bool, err error) {
returndata = false
var login = system.Login{Username: user}
this.db.Where("mail = ?", user).First(&login)
if login.ID <= 0 {
liblog.Log.Warn("user not found")
return
}
if login.Active {
output, _ := libpassword.Validate(login.Password, pass)
if output {
returndata = true
liblog.Log.Info("done")
} else {
liblog.Log.Warn("wrong password")
}
} else {
liblog.Log.Warn("not active")
}
return
}

View File

@ -0,0 +1,31 @@
package main
import (
"io/ioutil"
"gopkg.in/yaml.v2"
)
// Config of warehost webserver
type Config struct {
Database string `yaml:"database"`
Log struct {
Path string `yaml:"path"`
} `yaml:"log"`
DatabaseDebug bool `yaml:"databasedebug"`
FTPPath string `yaml:"data"`
Host string `yaml:"host"`
Web string `yaml:"web"`
Port int `yaml:"port"`
}
// ReadConfigFile reads a config models by path to a yml file
func ReadConfigFile(path string) *Config {
config := &Config{}
file, _ := ioutil.ReadFile(path)
err := yaml.Unmarshal(file, &config)
if err != nil {
panic(err)
}
return config
}

View File

@ -0,0 +1,9 @@
---
database: "host=localhost user=warehost dbname=warehost password=hallo sslmode=disable"
log:
path: test.log
databasedebug: false
port: 2222
data: /tmp/ftp/%d/
host: /tmp/ftp-domain/%s/
web: /tmp/ftp-web/%d/

333
cmd/warehost-ftp/driver.go Normal file
View File

@ -0,0 +1,333 @@
package main
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
ftpd "github.com/goftp/server"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
system "dev.sum7.eu/sum7/warehost/system"
)
type FileDriver struct {
RootPath string
db *gorm.DB
Perm ftpd.Perm
login system.Login
conn *ftpd.Conn
}
type FackFileInfo struct {
os.FileInfo
name string
}
func (f *FackFileInfo) Mode() os.FileMode {
return os.ModeDir
}
func (f *FackFileInfo) IsDir() bool {
return true
}
func (f *FackFileInfo) Name() string {
return f.name
}
func (f *FackFileInfo) Owner() string {
return "warehost"
}
func (f *FackFileInfo) Size() int64 {
return 0
}
func (f *FackFileInfo) ModTime() time.Time {
return time.Now()
}
func (f *FackFileInfo) Group() string {
return "http"
}
type FileInfo struct {
os.FileInfo
mode os.FileMode
owner string
group string
}
func (f *FileInfo) Mode() os.FileMode {
return f.mode
}
func (f *FileInfo) Owner() string {
return f.owner
}
func (f *FileInfo) Group() string {
return f.group
}
func (driver *FileDriver) chechLogin() {
if driver.login.ID <= 0 && driver.conn.IsLogin() {
driver.db.Where("mail = ?", driver.conn.LoginUser()).First(&driver.login)
fmt.Printf("Connection:%s:%d", driver.conn.LoginUser(), driver.login.ID)
}
}
func (driver *FileDriver) realPath(path string) (string, bool) {
driver.chechLogin()
paths := strings.Split(path, "/")
root := "/dev/null"
real := false
if len(paths) > 1 && driver.login.ID > 0 {
switch paths[1] {
case "data":
root = fmt.Sprintf(driver.RootPath, driver.login.ID)
paths = append([]string{paths[0]}, paths[2:]...)
real = true
}
}
return filepath.Join(append([]string{root}, paths...)...), real
}
func (driver *FileDriver) Init(conn *ftpd.Conn) {
driver.conn = conn
}
func (driver *FileDriver) ChangeDir(path string) error {
rPath, real := driver.realPath(path)
if !real {
return nil
}
f, err := os.Lstat(rPath)
if err != nil {
return err
}
if f.IsDir() {
return nil
}
return errors.New("Not a directory")
}
func (driver *FileDriver) Stat(path string) (ftpd.FileInfo, error) {
basepath, real := driver.realPath(path)
var f os.FileInfo
if real {
rPath, err := filepath.Abs(basepath)
if err != nil {
return nil, err
}
f, err = os.Lstat(rPath)
if err != nil {
return nil, err
}
} else {
f = &FackFileInfo{name: path}
}
mode, err := driver.Perm.GetMode(path)
if err != nil {
return nil, err
}
if f.IsDir() {
mode |= os.ModeDir
}
owner, err := driver.Perm.GetOwner(path)
if err != nil {
return nil, err
}
group, err := driver.Perm.GetGroup(path)
if err != nil {
return nil, err
}
return &FileInfo{f, mode, owner, group}, nil
}
func (driver *FileDriver) ListDir(path string, callback func(ftpd.FileInfo) error) error {
basepath, real := driver.realPath(path)
if real {
filepath.Walk(basepath, func(f string, info os.FileInfo, err error) error {
rPath, _ := filepath.Rel(basepath, f)
if rPath == info.Name() {
mode, err := driver.Perm.GetMode(rPath)
if err != nil {
return err
}
if info.IsDir() {
mode |= os.ModeDir
}
owner, err := driver.Perm.GetOwner(rPath)
if err != nil {
return err
}
group, err := driver.Perm.GetGroup(rPath)
if err != nil {
return err
}
err = callback(&FileInfo{info, mode, owner, group})
if err != nil {
return err
}
}
return nil
})
return nil
} else {
if path == "/" {
for _, i := range []string{"data", "web", "host"} {
err := callback(&FackFileInfo{name: i})
if err != nil {
return err
}
}
return nil
}
return errors.New("No path")
}
}
func (driver *FileDriver) DeleteDir(path string) error {
rPath, real := driver.realPath(path)
if !real {
return errors.New("Warehost folders are not deletable")
}
f, err := os.Lstat(rPath)
if err != nil {
return err
}
if f.IsDir() {
return os.Remove(rPath)
}
return errors.New("Not a directory")
}
func (driver *FileDriver) DeleteFile(path string) error {
rPath, real := driver.realPath(path)
if !real {
return errors.New("Warehost files are not deletable")
}
f, err := os.Lstat(rPath)
if err != nil {
return err
}
if !f.IsDir() {
return os.Remove(rPath)
}
return errors.New("Not a file")
}
func (driver *FileDriver) Rename(fromPath string, toPath string) error {
oldPath, realOld := driver.realPath(fromPath)
newPath, realNew := driver.realPath(toPath)
if !realOld || !realNew {
return errors.New("Warehost files/folders are not moveable")
}
return os.Rename(oldPath, newPath)
}
func (driver *FileDriver) MakeDir(path string) error {
rPath, real := driver.realPath(path)
if !real {
return errors.New("Warehost folders are not createable")
}
return os.Mkdir(rPath, os.ModePerm)
}
func (driver *FileDriver) GetFile(path string, offset int64) (int64, io.ReadCloser, error) {
rPath, real := driver.realPath(path)
if !real {
return 0, nil, errors.New("Warehost files are not downloadable")
}
f, err := os.Open(rPath)
if err != nil {
return 0, nil, err
}
info, err := f.Stat()
if err != nil {
return 0, nil, err
}
f.Seek(offset, os.SEEK_SET)
return info.Size(), f, nil
}
func (driver *FileDriver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {
rPath, real := driver.realPath(destPath)
if !real {
return 0, errors.New("Warehost files are not replaceable")
}
var isExist bool
f, err := os.Lstat(rPath)
if err == nil {
isExist = true
if f.IsDir() {
return 0, errors.New("A dir has the same name")
}
} else {
if os.IsNotExist(err) {
isExist = false
} else {
return 0, errors.New(fmt.Sprintln("Put File error:", err))
}
}
if appendData && !isExist {
appendData = false
}
if !appendData {
if isExist {
err = os.Remove(rPath)
if err != nil {
return 0, err
}
}
f, err := os.Create(rPath)
if err != nil {
return 0, err
}
defer f.Close()
bytes, err := io.Copy(f, data)
if err != nil {
return 0, err
}
return bytes, nil
}
of, err := os.OpenFile(rPath, os.O_APPEND|os.O_RDWR, 0660)
if err != nil {
return 0, err
}
defer of.Close()
_, err = of.Seek(0, os.SEEK_END)
if err != nil {
return 0, err
}
bytes, err := io.Copy(of, data)
if err != nil {
return 0, err
}
return bytes, nil
}
type FileDriverFactory struct {
RootPath string
db *gorm.DB
Perm ftpd.Perm
}
func (factory *FileDriverFactory) NewDriver() (ftpd.Driver, error) {
return &FileDriver{RootPath: factory.RootPath, db: factory.db, Perm: factory.Perm}, nil
}

62
cmd/warehost-ftp/main.go Normal file
View File

@ -0,0 +1,62 @@
package main
import (
"database/sql"
"flag"
ftpd "github.com/goftp/server"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
liblog "dev.sum7.eu/sum7/warehost/lib/log"
host "dev.sum7.eu/sum7/warehost/modul/host"
web "dev.sum7.eu/sum7/warehost/modul/web"
system "dev.sum7.eu/sum7/warehost/system"
)
var (
configFile string
dbconnection *gorm.DB
dbDB *sql.DB
config *Config
)
func main() {
var err error
flag.StringVar(&configFile, "c", "config.yml", "path of configuration file")
flag.Parse()
config = ReadConfigFile(configFile)
liblog.NewLogger(config.Log.Path)
// Main Databaseconnection
dbconnection, err = gorm.Open("postgres", config.Database)
if err != nil {
liblog.Log.Fatal("database connection: ", err)
}
defer dbconnection.Close()
dbconnection.Callback().Create().Remove("gorm:update_time_stamp")
dbconnection.Callback().Update().Remove("gorm:update_time_stamp")
dbconnection.SingularTable(true)
dbconnection.LogMode(config.DatabaseDebug)
//load system Models to database
system.SyncModels(dbconnection)
host.SyncModels(dbconnection)
web.SyncModels(dbconnection)
opt := &ftpd.ServerOpts{
Name: "",
Factory: &FileDriverFactory{RootPath: config.FTPPath, db: dbconnection, Perm: ftpd.NewSimplePerm("warehost", "http")},
Port: config.Port,
Auth: WarehostAuth{db: dbconnection},
}
// start ftp server
ftpServer := ftpd.NewServer(opt)
liblog.Log.Info("warehost-ftp")
err = ftpServer.ListenAndServe()
if err != nil {
liblog.Log.Fatal("Error starting server:", err)
}
}

43
cmd/warehost-host/caddy Normal file
View File

@ -0,0 +1,43 @@
# ID: {{.ID}} - Domain: {{.Domain.ID}} - Profil: {{.Domain.Profil.ID}}
# Login: {{.Domain.Profil.Login.ID}} -> {{.Domain.Profil.Login.Username}}
# {{.Subdomain}} - {{.Domain.FQDN}}
{{ define "domain" }}{{if .Subdomain}}{{.Subdomain}}.{{end}}{{.Domain.FQDN}}{{ end }}
{{ define "content"}}
{{if not .Redirect}}
gzip
{{if .Proxy}}
proxy / {{.Proxy}}
{{else}}
root /srv/http/domain/{{template "domain" .}}
{{if .PHP}}
# php not supported
{{end}}
{{end}}
{{else}}
redir https://{{.Redirect}}{uri}
{{end}}
{{end}}
{{if .SSL}}
{{if .SSLRedirect}}
http://{{template "domain".}} {
{{if not .Redirect}}
redir https://{{template "domain".}}{uri}
{{else}}
{{template "content" .}}
{{end}}
}
https://{{template "domain".}} {
{{template "content" .}}
}
{{else}}
http://{{template "domain" .}},https://{{template "domain".}} {
{{template "content" .}}
}
{{end}}
{{else}}
http://{{template "domain".}} {
{{template "content" .}}
}
{{end}}

View File

@ -0,0 +1,38 @@
package main
import (
"io/ioutil"
"gopkg.in/yaml.v2"
)
// Config of warehost webserver
type Config struct {
WarehostDatabase string `yaml:"warehostdatabase"`
Log struct {
Path string `yaml:"path"`
} `yaml:"log"`
DatabaseDebug bool `yaml:"databasedebug"`
Web struct {
Enable bool `yaml:"enable"`
ConfigurationTemplate string `yaml:"template"`
ConfigurationFile string `yaml:"config"`
} `yaml:"web"`
Database struct {
Enable bool `yaml:"enable"`
Type string `yaml:"type"`
Connection string `yaml:"connection"`
Prefix string `yaml:"prefix"`
} `yaml:"database"`
}
// ReadConfigFile reads a config models by path to a yml file
func ReadConfigFile(path string) *Config {
config := &Config{}
file, _ := ioutil.ReadFile(path)
err := yaml.Unmarshal(file, &config)
if err != nil {
panic(err)
}
return config
}

View File

@ -0,0 +1,14 @@
---
warehostdatabase: "host=localhost user=warehost dbname=warehost password=hallo sslmode=disable"
log:
path: test.log
databasedebug: false
web:
enable: true
template: caddy
config: /tmp/a
database:
enable: true
type: mysql
connection: "root:hallo@/mysql?interpolateParams=true"
prefix: "warehost_db"

153
cmd/warehost-host/main.go Normal file
View File

@ -0,0 +1,153 @@
package main
import (
"database/sql"
"flag"
"fmt"
"os"
"strconv"
"strings"
"text/template"
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
liblog "dev.sum7.eu/sum7/warehost/lib/log"
host "dev.sum7.eu/sum7/warehost/modul/host"
system "dev.sum7.eu/sum7/warehost/system"
)
var (
configFile string
dbconnection *gorm.DB
dbDB *sql.DB
config *Config
)
func main() {
var err error
flag.StringVar(&configFile, "c", "config.yml", "path of configuration file")
flag.Parse()
config = ReadConfigFile(configFile)
liblog.NewLogger(config.Log.Path)
// Main Databaseconnection
dbconnection, err = gorm.Open("postgres", config.WarehostDatabase)
if err != nil {
liblog.Log.Fatal("database connection: ", err)
}
defer dbconnection.Close()
dbconnection.Callback().Create().Remove("gorm:update_time_stamp")
dbconnection.Callback().Update().Remove("gorm:update_time_stamp")
dbconnection.SingularTable(true)
dbconnection.LogMode(config.DatabaseDebug)
//load system Models to database
system.SyncModels(dbconnection)
host.SyncModels(dbconnection)
// Configurate Webserver
if config.Web.Enable {
tmpl, err := template.ParseFiles(config.Web.ConfigurationTemplate)
if err != nil {
liblog.Log.Warning("load template: ", err)
}
file, err := os.OpenFile(config.Web.ConfigurationFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0660)
defer file.Close()
if err != nil {
liblog.Log.Warning("open configfile: ", err)
}
var web []*host.Web
dbconnection.Preload("Domain.Profil.Login").Order("length(subdomain) asc").Find(&web)
for _, item := range web {
if item.Domain.Active && item.Domain.Web && item.Domain.Profil.Login.Active {
err = tmpl.Execute(file, item)
if err != nil {
liblog.Log.Warning("write config: ", err)
}
}
}
}
// Configurate Database
if config.Database.Enable {
dbDB, err = sql.Open(config.Database.Type, config.Database.Connection)
if err != nil {
liblog.Log.Fatal("connection to mgmt db: ", err)
}
q, err := dbDB.Query(fmt.Sprintf("show databases LIKE \"%s%%\";", config.Database.Prefix))
if err != nil {
liblog.Log.Fatal("list current dbs in mgmt db: ", err)
}
defer q.Close()
var dbName string
currentDBS := map[int64]struct{}{}
for q.Next() {
q.Scan(&dbName)
dbName = strings.TrimPrefix(dbName, config.Database.Prefix)
i, err := strconv.ParseInt(dbName, 10, 64)
if err != nil {
liblog.Log.Warning("listed current dbs to id in mgmt db: ", err)
} else {
currentDBS[i] = struct{}{}
}
}
var dbs []*host.Database
if dbconnection.Find(&dbs).Error != nil {
liblog.Log.Warning("list in mgmt db: ", err)
} else {
liblog.Log.Info("count of mgmt db: ", len(dbs))
}
shouldDB := map[int64]*host.Database{}
for _, i := range dbs {
name := fmt.Sprintf("%s%d", config.Database.Prefix, i.ID)
shouldDB[i.ID] = i
_, err = dbDB.Exec("CREATE USER IF NOT EXISTS ?@'localhost' IDENTIFIED BY ?;", name, i.Password)
if err != nil {
liblog.Log.Warning("configurate user of mgmt db: ", err)
continue
}
_, err = dbDB.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", name))
if err != nil {
liblog.Log.Warning("configurate db of mgmt db: ", err)
continue
}
_, err = dbDB.Exec(fmt.Sprintf("GRANT ALL ON %s.* TO ?@'localhost' WITH GRANT OPTION;", name), name)
if err != nil {
liblog.Log.Warning("configurate permission db of mgmt db: ", err)
continue
}
if len(i.Password) > 1 {
_, err = dbDB.Exec("SET PASSWORD FOR ?@'localhost'=PASSWORD(?);", name, i.Password)
if err != nil {
liblog.Log.Info("configurate new password of mgmt db: ", name)
continue
} else {
i.Password = ""
dbconnection.Save(i)
}
}
liblog.Log.Info("configurate of mgmt db: ", name)
}
for id := range currentDBS {
if ok := shouldDB[id]; ok == nil {
name := fmt.Sprintf("%s%d", config.Database.Prefix, id)
_, err = dbDB.Exec(fmt.Sprintf("DROP USER IF EXISTS %s@'localhost';", name))
if err != nil {
liblog.Log.Info("drop of mgmt db: ", name, err)
} else {
liblog.Log.Warning("count of mgmt db: ", name)
dbDB.Query(fmt.Sprintf("DROP DATABASE IF EXISTS %s;", name))
}
}
}
}
}