summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHorus32015-02-10 08:33:42 +0100
committerHorus32015-02-10 08:33:42 +0100
commit8c30749613bcf1ce47fff0a6d1a60c34f91f01c4 (patch)
tree98354473d929cbc9749f08ea0afdf90993a01480
parentaf1f4677c685e8a2c4967ffa0350d314a6543db7 (diff)
downloadwebmon-8c30749613bcf1ce47fff0a6d1a60c34f91f01c4.tar.gz
Basic user control.
-rw-r--r--.gitignore1
-rw-r--r--app/controllers/app.go147
-rw-r--r--app/controllers/db.go84
-rw-r--r--app/controllers/url.go28
-rw-r--r--app/controllers/utilities.go100
-rw-r--r--app/init.go9
-rw-r--r--app/mailers/mail.go15
-rw-r--r--conf/app.conf11
8 files changed, 350 insertions, 45 deletions
diff --git a/.gitignore b/.gitignore
index dae67d0..70a8117 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
test-results/
tmp/
routes/
+*.swp
diff --git a/app/controllers/app.go b/app/controllers/app.go
index e76d76b..4aad9a3 100644
--- a/app/controllers/app.go
+++ b/app/controllers/app.go
@@ -1,6 +1,9 @@
package controllers
-import "github.com/revel/revel"
+import (
+ "github.com/garyburd/redigo/redis"
+ "github.com/revel/revel"
+)
type App struct {
*revel.Controller
@@ -9,3 +12,145 @@ type App struct {
func (c App) Index() revel.Result {
return c.Render()
}
+
+func (c App) PrintLogin(legacy bool) revel.Result {
+ return c.Render(legacy)
+}
+
+func (c App) PrintRegister() revel.Result {
+ return c.Render()
+}
+
+func (c App) Account() revel.Result {
+ return c.Render()
+}
+
+func (c App) Login(email string, legacy bool, user string, password string, passwordConfirm string) revel.Result {
+
+ if legacy {
+ // Show login form with username and password
+ c.Validation.Required(user).Message("Please enter a user name.")
+ c.Validation.Required(password).Message("Please enter a password.")
+ c.Validation.Required(confirm).Message("Please confirm your password.")
+ c.Validation.Required(password == passwordConfirm).Message("The passwords do not match.")
+ } else {
+ // Show login form only with email
+ c.Validation.Required(email).Message("Please provide a mail adress.")
+ }
+
+ if c.Validation.HasErrors() {
+ c.Validation.Keep()
+ c.FlashParams()
+ return c.Redirect(App.PrintLogin)
+ }
+
+ u := User{}
+ if legacy {
+ // do database lookup and show if its matched
+ db.Where("name = ?", user).First(&u)
+ c.Validation.Required(u.confirmed).Message("Your mail adress is not confirmed yet.")
+ c.Validation.Required(VerifyPassword(password, u.password)).Message("The user/password combination does not exists.")
+
+ if c.Validation.HasErrors() {
+ c.Validation.Keep()
+ c.FlashParams()
+ return c.Redirect(App.PrintLogin)
+ }
+
+ c.Session["login"] = "true"
+ c.Session["uid"] = u.id
+
+ return c.Redirect(App.Account)
+ } else {
+ db.Where("email = ?", email).First(&u)
+ // Get random string
+ key := RandomKey()
+ // Set key in redis
+ conn := pool.Get()
+ defer conn.Close()
+ _, err := conn.Do("SET", key, u.email, 86400)
+ // Send email with confirmation link
+ // TODO Implementing the function
+ SendConfirmationKey(email, key)
+
+ // TODO Print message that a mail was sent
+ return c.Redirect(App.PrintLogin)
+ }
+}
+
+func (c App) Confirm(key string) revel.Result {
+
+ c.Validation.Required(key).Message("No key provided.")
+
+ conn := pool.Get()
+ confirmKey, err := conn.Do("GET", key)
+ c.Validation.Required(err == nil).Message("Oops, there is currently an internal problem. Please check later again.")
+ c.Validation.Required(confirmKey).Message("Key does not seem to be valid.")
+
+ _, _ = conn.Do("DEL", key)
+
+ if c.Validation.HasErrors() {
+ c.Validation.Keep()
+ c.FlashParams()
+ return c.Redirect(App.PrintLogin)
+ }
+
+ u := User{}
+ db.Where("email = ?", email).First(&u)
+
+ if u.confirmed == false {
+ // E-Mail is now confirmed
+ u.confirmed = true
+ db.Save(&u)
+ }
+
+ c.Session["login"] = "true"
+ c.Session["uid"] = u.id
+
+ return c.Redirect(App.Account)
+}
+
+func (c App) Register(email, confirmEmail, user, password, confirmPassword string) revel.Result {
+
+ c.Validation.Required(email).Messagel("Please provide a mail adress.")
+ c.Validation.Required(email == confirmEmail).Messagel("The mail adresses do not match.")
+ c.Validation.Required(user).Messagel("Please provide a user name.")
+
+ if password != "" {
+ c.Validation.Required(password == confirmPassword).Messagel("Passwords do not match.")
+ }
+
+ if c.Validation.HasErrors() {
+ c.Validation.Keep()
+ c.FlashParams()
+ return c.Redirect(App.PrintRegister)
+ }
+
+ p := HashPassword(password)
+ u := User{
+ Name: user,
+ Email: email,
+ Password: p,
+ Confirmed: false,
+ Alerts: []Alert{{Email: email}},
+ }
+
+ db.NewRecord(user)
+ db.Create(&user)
+ db.Save(&user)
+
+ // Create key to confirm mail adress
+ key := RandomKey()
+
+ // Redis
+ conn := pool.Get()
+ defer conn.Close()
+ _, err := conn.Do("SET", key, email)
+
+ // Send email with confirmation link
+ // TODO Implementing the function
+ SendConfirmationKey(email, key)
+ c.Flash.Success("A mail with a confirmation link was sent. Please confirm your mail adress now.")
+
+ return c.Redirect(App.PrintLogin)
+}
diff --git a/app/controllers/db.go b/app/controllers/db.go
index 9607f24..f6a535e 100644
--- a/app/controllers/db.go
+++ b/app/controllers/db.go
@@ -4,40 +4,96 @@ import (
"github.com/jinzhu/gorm"
_ "github.com/mattn/go-sqlite3"
"github.com/revel/revel"
+ "time"
)
type User struct {
- Id int64
- Email string
- Name string
- Password string
+ Id int64
+ Email string `sql:"unique"`
+ Name string `sql:"unique"`
+ Password string
+ Confirmed bool
+ Alerts []Alert
+ CreatedAt time.Time
+ DeletedAt time.Time
+ UpdatedAt time.Time
+}
+
+// Multiple accounts which are alerted
+type Alert struct {
+ Id int64
+ UserId int64
+ Email string
+ CreatedAt time.Time
+ DeletedAt time.Time
+ UpdatedAt time.Time
}
type Job struct {
+ Id int64
+ UserId int64
+ Name string
+ Url string
+ Versions []Version
+ Diff string
+ DiffLen int64
+ CreatedAt time.Time
+ DeletedAt time.Time
+ UpdatedAt time.Time
}
-var DB *gorm.DB
+// Save history version of jobs
+type Version struct {
+ Id int64
+ JobId int64
+ Content string
+ Hash string
+ CreatedAt time.Time
+ DeletedAt time.Time
+ UpdatedAt time.Time
+}
-func DBInit() *sql.DB {
+var db = DBInit()
+
+func DBInit() *gorm.DB {
// Open database handler
- db, err := gorm.Open("sqlite3", "/tmp/gorm.db")
+ db, err := gorm.Open(revel.Config.String("db.driver"), revel.Config.String("db.spec"))
+
+ // Set database logging to TRACE
+ db.LogMode(true)
+ db.SetLogger(gorm.Logger{revel.TRACE})
// Ping database to check if up
if err = db.DB().Ping(); err != nil {
revel.ERROR.Panicf("Failed to init database. %s \n", err)
}
+ defer db.Close()
// Set Pool connections
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)
- // Create tables defined as struct
- db.CreateTable(&User{})
- db.CreateTable(&Job{})
+ // Create tables which are defined as struct
+ db.Debug().CreateTable(&User{})
+ db.Debug().CreateTable(&Alert{})
+ db.Debug().CreateTable(&Job{})
+ db.Debug().CreateTable(&Version{})
+
+ // Automigration
+ db.Debug().AutoMigrate(&User{}, &Job{}, &Version{}, &Alert{})
+
+ // Add index on email and name to enforce uniqueness
+ db.Debug().Model(&User{}).AddUniqueIndex("idx_user_email", "email")
+ db.Debug().Model(&User{}).AddUniqueIndex("idx_user_name", "name")
+
+ // Unique index on alert to ensure every user can specify not multiple equally alerts
+ db.Debug().Model(&Alert{}).AddUniqueIndex("idx_alert_email_userid", "email", "user_id")
+
+ // Unique index to ensure every user can have only one job with the same name
+ db.Debug().Model(&Job{}).AddUniqueIndex("idx_job_name_userid", "name", "user_id")
- // Add index on email
- db.Model(&User{}).AddIndex("idx_user_email", "email")
+ // Makes hash unique
+ // db.Debug().Model(&Version{}).AddUniqueIndex("idx_version_hash", "hash")
- // Make database handler public
- DB = db
+ return db
}
diff --git a/app/controllers/url.go b/app/controllers/url.go
deleted file mode 100644
index 516f6a6..0000000
--- a/app/controllers/url.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package controllers
-
-import (
- "crypto/md5"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
-)
-
-func HashUrl(url string) (string, error) {
- response, err := http.Get(url)
- if err != nil {
- return "Get request failed.", err
- }
-
- defer response.Body.Close()
- contents, err := ioutil.ReadAll(response.Body)
- if er != nil {
- return "Reading body failed.", err
- }
-
- h := md5.New()
- io.WriteString(h, string(contents))
- hash := fmt.Sprintf("%x", h.Sum(nil))
-
- return hash, nil
-}
diff --git a/app/controllers/utilities.go b/app/controllers/utilities.go
new file mode 100644
index 0000000..2eae775
--- /dev/null
+++ b/app/controllers/utilities.go
@@ -0,0 +1,100 @@
+package controllers
+
+import (
+ "crypto/md5"
+ "fmt"
+ "github.com/garyburd/redigo/redis"
+ "github.com/tanema/revel_mailer"
+ "golang.org/x/crypto/bcrypt"
+ "io"
+ "io/ioutil"
+ "math/rand"
+ "net/http"
+ "time"
+)
+
+// Returns the content of a webpage as string
+func Get(url string) (string, error) {
+ response, err := http.Get(url)
+ if err != nil {
+ return "Get request failed.", err
+ }
+
+ defer response.Body.Close()
+ contents, err := ioutil.ReadAll(response.Body)
+ if er != nil {
+ return "Reading body failed.", err
+ }
+
+ return string(contents), nil
+}
+
+// Hashs and returns a string (md5)
+func Hash(content string) string {
+ h := md5.New()
+ io.WriteString(h, content)
+ hash := fmt.Sprintf("%x", h.Sum(nil))
+
+ return hash
+}
+
+// Creates a random string
+func RandomKey() string {
+ letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
+ key := make([]rune, 40)
+ for i := range key {
+ key[i] = letters[rand.Intn(len(letters))]
+ }
+
+ return string(key)
+}
+
+var pool = newPool()
+
+// Creates a pool with connections to Redis
+func newPool() *redis.Pool {
+ return &redis.Pool{
+ MaxIdle: 3,
+ IdleTimeout: 240 * time.Second,
+ Dial: func() (redis.Conn, error) {
+ //c, err := redis.Dial("tcp", ":6379")
+ if revel.Config.Bool("cache.redis") {
+ // If we use redis as cache we reuse the config part
+ c, err := redis.Dial("tcp", revel.Config.String("cache.hosts"))
+ } else {
+ // Otherwise we use our own configuration
+ c, err := redis.Dial("tcp", revel.Config.String("redis.server")+":"+revel.Config.String("redis.port"))
+ }
+ if err != nil {
+ return nil, err
+ }
+ return c, err
+ },
+ TestOnBorrow: func(c redis.Conn, t time.Time) error {
+ _, err := c.Do("PING")
+ return err
+ },
+ }
+}
+
+// Hashs password with bcrypt and returns the string
+func HashPassword(password string) (string, error) {
+ if password == "" {
+ return nil, nil
+ }
+ p := []byte(password)
+ hash, err := bcrypt.GenerateFromPassword(p, 10)
+ if err != nil {
+ return nil, err
+ }
+ return string(hash)
+}
+
+// Verify password and hash
+func VerifyPassword(password, hash string) (bool, error) {
+ err := bcrypt.CompareHashAndPassword(hash, password)
+ if err != nil {
+ return false, err
+ }
+ return true, nil
+}
diff --git a/app/init.go b/app/init.go
index 2305d73..b13043a 100644
--- a/app/init.go
+++ b/app/init.go
@@ -1,6 +1,10 @@
package app
-import "github.com/revel/revel"
+import (
+ "github.com/anonx/cachesession"
+ "github.com/cbonello/revel-csrf"
+ "github.com/revel/revel"
+)
func init() {
// Filters is the default set of global filters.
@@ -9,8 +13,9 @@ func init() {
revel.RouterFilter, // Use the routing table to select the right Action
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
revel.ParamsFilter, // Parse parameters into Controller.Params.
- revel.SessionFilter, // Restore and write the session cookie.
+ cachesession.Filter, // Restore and write the session cookie.
revel.FlashFilter, // Restore and write the flash cookie.
+ csrf.CSRFFilter, // CSRF protection
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
revel.I18nFilter, // Resolve the requested language
HeaderFilter, // Add some security based headers
diff --git a/app/mailers/mail.go b/app/mailers/mail.go
new file mode 100644
index 0000000..1c3a198
--- /dev/null
+++ b/app/mailers/mail.go
@@ -0,0 +1,15 @@
+package mailers
+
+import "github.com/tanema/revel_mailer"
+
+type Mailer struct {
+ revel_mailer.Mailer
+}
+
+func (u Mailer) SendConfirmationKey(email, key) {
+ return u.Send(revel_mailer.H{
+ "subject": "Confirmation Key",
+ "to": []string{email},
+ "key": key,
+ })
+}
diff --git a/conf/app.conf b/conf/app.conf
index c923753..0c2dbf3 100644
--- a/conf/app.conf
+++ b/conf/app.conf
@@ -15,6 +15,17 @@ app.name = webmon
# into your application
app.secret = vQmfAomzCfewNr0rJlmvQ9Dv42wVMy6x3l5A0X3MsGgAgl95huCHW4fO1hsjkjnn
+db.import = "github.com/mattn/go-sqlite3"
+db.driver = "sqlite3"
+db.spec = "/tmp/gorb.db"
+
+redis.server = "127.0.0.1"
+redis.port = "6379"
+
+mail.host = localhost
+mail.port = 25
+mail.from = webmon
+mail.user = webmon@iamfabulous.de
# The IP address on which to listen.
http.addr =