Skip to content

Commit e8add21

Browse files
committed
-
1 parent 22de7e0 commit e8add21

12 files changed

+267
-52
lines changed

CHANGES

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
1.0.0-release-build230419
2+
first release
3+
4+
1.1.0-release-build300419
5+
- salted and hashed passwords in database
6+
- send random recovery passwords on mail

README.md

+24-3
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,34 @@ Basic server side web application with the following functionality:
1212
- config file in JSON format
1313
- database generated from file or model (with [Mysql Workbench](https://dev.mysql.com/downloads/workbench/))
1414
- responsive interface for mobile access, browser back button disabled
15+
- salted and hashed passwords in database
1516

1617
### Integrations
1718

1819
The skeleton is based on [W3CSS](https://www.w3schools.com/w3css/), a very lightweight CSS framework and is designed to integrate with [JQuery](https://jquery.com/), [Angular](https://angular.io/), [Fontawesome](https://fontawesome.com/v4.7.0/icons/) and some useful libraries like [Datetime picker](https://trentrichardson.com/examples/timepicker/) and [Google Charts](https://developers.google.com/chart/) (see `ui/head.html` file).
1920

21+
### Compilation modes
22+
23+
#### Plaint text passwords
24+
25+
go build -tags="plain"
26+
27+
Passwords are stored in plain text in the database and sent as plain text on mail to recovery (unrecomanded).
28+
29+
#### Hashed passwords
30+
31+
go build -tags="sha1 random"
32+
33+
Passwords are stored hashed in the database and sent as random on mail to recovery (recomanded).
34+
35+
#### Salted and hashed passwords
36+
37+
go build -tags="saltsha1 random"
38+
39+
Passwords are stored salted and hashed in the database and sent as random on mail to recovery (highly recomanded).
40+
41+
The tag `random` mean send password as random string.
42+
2043
### How is look like?
2144

2245
#### Signup screen
@@ -134,6 +157,4 @@ Look carefully over **TODO** and **NOTE** comments because you may need to adjus
134157

135158
### Known issues
136159

137-
The skeleton is designed to work with HTML5 so make sure your browser support this.
138-
139-
For now the passwords are stored in plain text in the database. Add your own security or wait until will be available soon in the next version.
160+
The skeleton is designed to work with HTML5 so make sure your browser support this.

database.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ CREATE TABLE IF NOT EXISTS `skel`.`user` (
22
`user_id` INT NOT NULL AUTO_INCREMENT,
33
`name` VARCHAR(45) NULL,
44
`email` VARCHAR(50) NULL,
5-
`password` VARCHAR(16) NULL,
5+
`password` VARCHAR(128) NULL,
66
`active` TINYINT(1) NULL DEFAULT 1,
77
PRIMARY KEY (`user_id`),
88
UNIQUE INDEX `email_UNIQUE` (`email` ASC))

db_user.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// +build plain
2+
13
package main
24

35
import (
@@ -36,9 +38,9 @@ func sqlGetUser(u *User) error {
3638
return nil
3739
}
3840

39-
// Create new user.
41+
// Create new user identifyed by name, email and password.
4042
func sqlInsert(u *User) error {
41-
if _, err := db.Exec("INSERT user SET name=?,email=?,password=?", &u.Name, &u.Email, &u.Password); err != nil {
43+
if _, err := db.Exec("INSERT user SET name=?, email=?, password=?", &u.Name, &u.Email, &u.Password); err != nil {
4244
log.Println(err)
4345
return err
4446
}
@@ -54,9 +56,9 @@ func sqlUpdateUser(u *User) error {
5456
return nil
5557
}
5658

57-
// Delete a user identifyed by email and password.
59+
// Delete a user identifyed by email.
5860
func sqlDeleteUser(u *User) error {
59-
if _, err := db.Exec("DELETE FROM user WHERE email=? AND password=?", u.Email, u.Password); err != nil {
61+
if _, err := db.Exec("DELETE FROM user WHERE email=?", u.Email); err != nil {
6062
log.Println(err)
6163
return err
6264
}

db_user_salt_sha1.go

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// +build saltsha1
2+
3+
package main
4+
5+
import (
6+
"log"
7+
8+
_ "github.com/go-sql-driver/mysql"
9+
)
10+
11+
// NOTE user management
12+
13+
// Count users from database.
14+
func sqlUserCount() (error, int) {
15+
var n int
16+
if err := db.QueryRow("SELECT COUNT(*) from user").Scan(&n); err != nil {
17+
log.Println(err)
18+
return err, 0
19+
}
20+
return nil, n
21+
}
22+
23+
// Get a user identifyed by email and password.
24+
func sqlAuthenticateUser(u *User) error {
25+
if err := db.QueryRow("SELECT * FROM user WHERE email=? AND password=SHA1(?)", u.Email, u.Password+salt).Scan(&u.Id, &u.Name, &u.Email, &u.Password, &u.isActive); err != nil {
26+
log.Println(err)
27+
return err
28+
}
29+
return nil
30+
}
31+
32+
// Get the user identifyed by email.
33+
func sqlGetUser(u *User) error {
34+
if err := db.QueryRow("SELECT * FROM user WHERE email=?", u.Email).Scan(&u.Id, &u.Name, &u.Email, &u.Password, &u.isActive); err != nil {
35+
log.Println(err)
36+
return err
37+
}
38+
return nil
39+
}
40+
41+
// Create new user identifyed by name, email and password.
42+
func sqlInsert(u *User) error {
43+
u.Password += salt
44+
if _, err := db.Exec("INSERT user SET name=?, email=?, password=SHA1(?)", &u.Name, &u.Email, &u.Password); err != nil {
45+
log.Println(err)
46+
return err
47+
}
48+
return nil
49+
}
50+
51+
// Update a user identifyed by email and password.
52+
func sqlUpdateUser(u *User) error {
53+
if _, err := db.Exec("UPDATE user SET name=?, password=SHA1(?) WHERE email=?", u.Name, u.Password+salt, u.Email); err != nil {
54+
log.Println(err)
55+
return err
56+
}
57+
return nil
58+
}
59+
60+
// Delete a user identifyed by email.
61+
func sqlDeleteUser(u *User) error {
62+
if _, err := db.Exec("DELETE FROM user WHERE email=?", u.Email); err != nil {
63+
log.Println(err)
64+
return err
65+
}
66+
return nil
67+
}

db_user_sha1.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// +build sha1
2+
3+
package main
4+
5+
import (
6+
"log"
7+
8+
_ "github.com/go-sql-driver/mysql"
9+
)
10+
11+
// NOTE user management
12+
13+
// Count users from database.
14+
func sqlUserCount() (error, int) {
15+
var n int
16+
if err := db.QueryRow("SELECT COUNT(*) from user").Scan(&n); err != nil {
17+
log.Println(err)
18+
return err, 0
19+
}
20+
return nil, n
21+
}
22+
23+
// Get a user identifyed by email and password.
24+
func sqlAuthenticateUser(u *User) error {
25+
if err := db.QueryRow("SELECT * FROM user WHERE email=? AND password=SHA1(?)", u.Email, u.Password).Scan(&u.Id, &u.Name, &u.Email, &u.Password, &u.isActive); err != nil {
26+
log.Println(err)
27+
return err
28+
}
29+
return nil
30+
}
31+
32+
// Get the user identifyed by email.
33+
func sqlGetUser(u *User) error {
34+
if err := db.QueryRow("SELECT * FROM user WHERE email=?", u.Email).Scan(&u.Id, &u.Name, &u.Email, &u.Password, &u.isActive); err != nil {
35+
log.Println(err)
36+
return err
37+
}
38+
return nil
39+
}
40+
41+
// Create new user identifyed by name, email and password.
42+
func sqlInsert(u *User) error {
43+
if _, err := db.Exec("INSERT user SET name=?, email=?, password=SHA1(?)", &u.Name, &u.Email, &u.Password); err != nil {
44+
log.Println(err)
45+
return err
46+
}
47+
return nil
48+
}
49+
50+
// Update a user identifyed by email and password.
51+
func sqlUpdateUser(u *User) error {
52+
if _, err := db.Exec("UPDATE user SET name=?, password=SHA1(?) WHERE email=?", u.Name, u.Password, u.Email); err != nil {
53+
log.Println(err)
54+
return err
55+
}
56+
return nil
57+
}
58+
59+
// Delete a user identifyed by email.
60+
func sqlDeleteUser(u *User) error {
61+
if _, err := db.Exec("DELETE FROM user WHERE email=?", u.Email); err != nil {
62+
log.Println(err)
63+
return err
64+
}
65+
return nil
66+
}

main.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919

2020
// TODO application version
2121
// https://semver.org/#semantic-versioning-200
22-
const SW_VERSION = "1.0.0-release-build260419"
22+
const SW_VERSION = "1.1.0-release-build300419"
2323

2424
// NOTE session cookie name
2525
const SESSID = "SESSID"
@@ -66,12 +66,17 @@ var httpsEnabled = flag.Bool("https-enabled", false, "enable https server")
6666

6767
var config = &Config{}
6868
var templ = template.New("templ").Delims("[[", "]]") // integrate with angular
69-
// core routers
70-
var router = mux.NewRouter() // main router
69+
var router = mux.NewRouter() // main router
70+
71+
// NOTE this is the salt for secured passwords
72+
const salt = "super-secret-key"
73+
74+
// NOTE random recovery password length
75+
const token_len = 8
7176

7277
var (
7378
// key must be 16, 24 or 32 bytes long (AES-128, AES-192 or AES-256)
74-
key = []byte("super-secret-key")
79+
key = []byte(salt)
7580
store = sessions.NewCookieStore(key)
7681
)
7782

model/skel_full.mwb

-9 Bytes
Binary file not shown.

ui/user.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
<h3>Edit user</h3>
1313
<form method="post" action="">
1414
<p><input type="text" placeholder="Name" name="name" value="[[.Name]]" title="Name" autofocus>
15-
<p><input type="password" placeholder="Password" name="password" id="password" value="[[.Password]]" title="Password">
16-
<p><input type="password" placeholder="Confirm password" name="password" id="confirm_password" value="[[.Password]]" title="Confirm password">
15+
<p><input type="password" placeholder="Password" name="password" id="password" value="" title="Password">
16+
<p><input type="password" placeholder="Confirm password" name="password" id="confirm_password" value="" title="Confirm password">
1717
<p><button type="submit" name="submit" value="save">Save</button>
1818
</form>
1919
<hr>

ui_reset.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// +build plain
2+
3+
package main
4+
5+
import (
6+
"log"
7+
"net/http"
8+
9+
gomail "gopkg.in/gomail.v2"
10+
)
11+
12+
func reset(w http.ResponseWriter, r *http.Request) {
13+
switch r.Method {
14+
case "GET":
15+
templ.ExecuteTemplate(w, "reset", nil)
16+
case "POST":
17+
var user User
18+
user.Email = r.FormValue("email")
19+
if err := sqlGetUser(&user); err != nil {
20+
http.Error(w, http.StatusText(http.StatusNotAcceptable), http.StatusNotAcceptable)
21+
return
22+
}
23+
log.Println(user.Password)
24+
return
25+
// https://stackoverflow.com/a/24431749
26+
mail := gomail.NewMessage()
27+
mail.SetAddressHeader("From", config.SMTP.User, config.SMTP.Name)
28+
mail.SetAddressHeader("To", r.FormValue("email"), "")
29+
mail.SetHeader("Subject", "Check your password request")
30+
mail.SetBody("text/html", user.Password)
31+
dialer := gomail.NewPlainDialer(config.SMTP.Server, config.SMTP.Port, config.SMTP.User, config.SMTP.Password)
32+
//dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
33+
if err := dialer.DialAndSend(mail); err != nil {
34+
log.Println(err)
35+
}
36+
w.Header().Set("Content-Type", "text/html; charset=utf-8")
37+
w.Write([]byte("A mail with instructions was send, read and <a href=\"/\">sign in</a>"))
38+
}
39+
}

ui_reset_random.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// +build random
2+
3+
package main
4+
5+
import (
6+
"log"
7+
"net/http"
8+
9+
"github.com/geosoft1/token"
10+
11+
gomail "gopkg.in/gomail.v2"
12+
)
13+
14+
func reset(w http.ResponseWriter, r *http.Request) {
15+
switch r.Method {
16+
case "GET":
17+
templ.ExecuteTemplate(w, "reset", nil)
18+
case "POST":
19+
var user User
20+
user.Email = r.FormValue("email")
21+
if err := sqlGetUser(&user); err != nil {
22+
http.Error(w, http.StatusText(http.StatusNotAcceptable), http.StatusNotAcceptable)
23+
return
24+
}
25+
// TODO reset password to random, uncomment the following 4 lines
26+
user.Password = token.GetToken(token_len)
27+
if err := sqlUpdateUser(&user); err != nil {
28+
http.Error(w, http.StatusText(http.StatusNotAcceptable), http.StatusNotAcceptable)
29+
return
30+
}
31+
log.Println(user.Password)
32+
return
33+
// https://stackoverflow.com/a/24431749
34+
mail := gomail.NewMessage()
35+
mail.SetAddressHeader("From", config.SMTP.User, config.SMTP.Name)
36+
mail.SetAddressHeader("To", r.FormValue("email"), "")
37+
mail.SetHeader("Subject", "Check your password request")
38+
mail.SetBody("text/html", user.Password)
39+
dialer := gomail.NewPlainDialer(config.SMTP.Server, config.SMTP.Port, config.SMTP.User, config.SMTP.Password)
40+
//dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
41+
if err := dialer.DialAndSend(mail); err != nil {
42+
log.Println(err)
43+
}
44+
w.Header().Set("Content-Type", "text/html; charset=utf-8")
45+
w.Write([]byte("A mail with instructions was send, read and <a href=\"/\">sign in</a>"))
46+
}
47+
}

0 commit comments

Comments
 (0)