3 Commits cc06aba216 ... 881ee976a1

Author SHA1 Message Date
  Adam 881ee976a1 limit progress to 100% 4 years ago
  Adam 4cb8297177 sign up 4 years ago
  Adam ca4d55b8ec show signup form 4 years ago
10 changed files with 147 additions and 6 deletions
  1. 0 1
      accounts/login.go
  2. 93 0
      accounts/signup.go
  3. 17 3
      db/db.go
  4. 7 1
      front/capnproto.go
  5. 14 1
      front/html.go
  6. 3 0
      front/renderer.go
  7. 1 0
      go.mod
  8. 2 0
      go.sum
  9. 10 0
      libamuse/qr.go
  10. 0 0
      libamuse/serie.go

+ 0 - 1
accounts/login.go

@@ -19,7 +19,6 @@ import (
 type AuthData struct {
 	username   string
 	password   string
-	passRepeat string
 	sfa        string
 	remember   bool
 }

+ 93 - 0
accounts/signup.go

@@ -0,0 +1,93 @@
+package accounts
+
+import (
+	"notabug.org/apiote/amuse/db"
+
+	"encoding/base64"
+	"errors"
+	"fmt"
+	"math/rand"
+	"strconv"
+	"strings"
+
+	"golang.org/x/crypto/argon2"
+	"notabug.org/apiote/gott"
+)
+
+func findNoUser(args ...interface{}) (interface{}, error) {
+	authData := args[0].(*AuthData)
+	authResult := args[1].(*AuthResult)
+	user, err := db.GetUser(authData.username)
+	authResult.user = user
+	if _, ok := err.(db.EmptyError); ok {
+		err = nil
+	} else if err == nil {
+		err = AuthError{
+			Err: errors.New("User exists"),
+		}
+	}
+	return gott.Tuple(args), err
+}
+
+func prepareSalt(args ...interface{}) (interface{}, error) {
+	argon := args[2].(*Argon)
+	salt := make([]byte, 16)
+	_, err := rand.Read(salt)
+	argon.salt = salt
+	return gott.Tuple(args), err
+}
+
+func hashPassword(args ...interface{}) interface{} {
+	authData := args[0].(*AuthData)
+	argon := args[2].(*Argon)
+	password := authData.password
+
+	hash := argon2.IDKey([]byte(password), argon.salt, 1, 64*1024, 4, 32)
+	b64Salt := base64.RawStdEncoding.EncodeToString(argon.salt)
+	b64Hash := base64.RawStdEncoding.EncodeToString(hash)
+	format := "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s"
+	fullHash := fmt.Sprintf(format, argon2.Version, 64*1024, 1, 4, b64Salt, b64Hash)
+	authData.password = fullHash
+	return gott.Tuple(args)
+}
+
+func createRecoveryCodes(args ...interface{}) interface{} {
+	authData := args[0].(*AuthData)
+	sfaSecret := authData.sfa
+	if sfaSecret != "" {
+		result := args[1].(*AuthResult)
+		codes := []string{}
+		for i := 0; i < 12; i++ {
+			code := rand.Int63n(999999999999)
+			codeStr := strconv.FormatInt(code, 10)
+			codes = append(codes, codeStr)
+		}
+		result.recoveryCodesRaw = strings.Join(codes, ",")
+	}
+	return gott.Tuple(args)
+}
+
+func insertUser(args ...interface{}) (interface{}, error) {
+	authData := args[0].(*AuthData)
+	result := args[1].(*AuthResult)
+	sfaSecret := authData.sfa
+	err := db.InsertUser(authData.username, authData.password, sfaSecret, result.recoveryCodesRaw)
+	return gott.Tuple(args), err
+}
+
+func Signup(username, password, sfaSecret string) (string, error) {
+	r, err := gott.
+		NewResult(gott.Tuple{&AuthData{username: username, password: password,
+			sfa: sfaSecret}, &AuthResult{}, &Argon{}}).
+		Bind(findNoUser).
+		Bind(prepareSalt).
+		Map(hashPassword).
+		Map(createRecoveryCodes).
+		Bind(insertUser).
+		Finish()
+
+	if err != nil {
+		return "", err
+	}
+	return r.(gott.Tuple)[1].(*AuthResult).token, err
+}

+ 17 - 3
db/db.go

@@ -59,7 +59,7 @@ func Migrate() error {
 	if err != nil && err.Error() != "table cache already exists" {
 		return err
 	}
-	_, err = db.Exec(`create table users(username text primary key, password text, sfa text, avatar blob, avatar_small blob, is_admin bool, recovery_codes text, timezone text)`)
+	_, err = db.Exec(`create table users(username text primary key, password text, sfa text, is_admin bool, recoveryCodes text, avatar blob, avatar_small blob, timezone text)`)
 	if err != nil && err.Error() != "table users already exists" {
 		return err
 	}
@@ -115,6 +115,22 @@ func MakeAdmin(username string) error {
 	return nil
 }
 
+func InsertUser(username, password, sfaSecret, recoveryCodes string) error {
+	db, err := sql.Open("sqlite3", utils.DataHome+"/amuse.db")
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "DB open err\n")
+		return err
+	}
+	defer db.Close()
+	_, err = db.Exec("insert into users values(?, ?, ?, 0, ?, '', '', 'UTC')", username, password, sfaSecret, recoveryCodes)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Inert err %v\n", err)
+		return err
+	}
+
+	return nil
+}
+
 func GetUser(username string) (*User, error) {
 	db, err := sql.Open("sqlite3", utils.DataHome+"/amuse.db")
 	if err != nil {
@@ -333,7 +349,6 @@ func AddToExperiences(username, itemId string, itemType datastructure.ItemType,
 		return 0, err
 	}
 
-
 	tx.Commit()
 
 	return int(insertedRowsNumber - deletedRowsNumber), nil
@@ -462,7 +477,6 @@ func IsOnWantList(username, itemId string, itemType datastructure.ItemType) (boo
 }
 
 func SaveCacheItem(itemType datastructure.ItemType, itemId string, itemInfo datastructure.ItemInfo, refs int) error {
-	fmt.Println(refs)
 	if refs == 0 {
 		return nil
 	}

+ 7 - 1
front/capnproto.go

@@ -6,6 +6,8 @@ import (
 	"notabug.org/apiote/amuse/datastructure"
 
 	"golang.org/x/text/language"
+
+	"github.com/pquerna/otp"
 )
 
 func TODO(message string) interface{} {
@@ -37,7 +39,6 @@ func (CapnprotoRenderer) RenderPerson(person *tmdb.Person, languages []language.
 func (CapnprotoRenderer) RenderBook(book wikidata.Book, languages []language.Tag) string {
 	return TODO("implement CapnprotoRenderer.RenderBook").(string)
 }
-
 func (CapnprotoRenderer) RenderBookSerie(bookSerie wikidata.BookSerie, languages []language.Tag) string {
 	return TODO("implement CapnprotoRenderer.RenderBookSerie").(string)
 }
@@ -55,6 +56,11 @@ func (CapnprotoRenderer) RenderLogin(languages []language.Tag, err error, target
 	return TODO("implement CapnprotoRenderer.RenderLogin").(string)
 }
 
+func (CapnprotoRenderer) RenderSignup(languages []language.Tag, err error, target *otp.Key) string {
+	// todo throw Wrong Accept
+	return TODO("implement CapnprotoRenderer.RenderSignup").(string)
+}
+
 func (CapnprotoRenderer) RenderWatchlist(watchlist datastructure.Watchlist, languages []language.Tag) string {
 	return TODO("implement CapnprotoRenderer.RenderWatchlist").(string)
 }

+ 14 - 1
front/html.go

@@ -2,17 +2,20 @@ package front
 
 import (
 	"notabug.org/apiote/amuse/accounts"
+	"notabug.org/apiote/amuse/datastructure"
 	"notabug.org/apiote/amuse/i18n"
 	"notabug.org/apiote/amuse/tmdb"
 	"notabug.org/apiote/amuse/utils"
 	"notabug.org/apiote/amuse/wikidata"
-	"notabug.org/apiote/amuse/datastructure"
 
 	"bytes"
 	"golang.org/x/text/language"
 	"html/template"
+	"net/url"
 	"strings"
 	"time"
+
+	"github.com/pquerna/otp"
 )
 
 type RenderData struct {
@@ -138,6 +141,16 @@ func (HtmlRenderer) RenderLogin(languages []language.Tag, authError error, targe
 	return render(languages, data, "login")
 }
 
+func (HtmlRenderer) RenderSignup(languages []language.Tag, authError error, key *otp.Key) string {
+	secret := struct {
+		Secret string
+		Url    string
+	}{key.Secret(), url.QueryEscape(key.URL())}
+	data := RenderData{Data: secret}
+	data.State.Error = authError
+	return render(languages, data, "signup")
+}
+
 func (r HtmlRenderer) RenderWatchlist(watchlist datastructure.Watchlist, languages []language.Tag) string {
 	data := RenderData{Data: watchlist}
 	data.State.User = r.user

+ 3 - 0
front/renderer.go

@@ -7,6 +7,8 @@ import (
 	"notabug.org/apiote/amuse/datastructure"
 
 	"golang.org/x/text/language"
+
+	"github.com/pquerna/otp"
 )
 
 type NoSuchRendererError struct {
@@ -28,6 +30,7 @@ type Renderer interface {
 	RenderAbout([]language.Tag) string
 	RenderErrorPage(int, []language.Tag) string
 	RenderLogin([]language.Tag, error, string) string
+	RenderSignup([]language.Tag, error, *otp.Key) string
 	RenderWatchlist(datastructure.Watchlist, []language.Tag) string
 	RenderTvQueue(datastructure.TvQueue, []language.Tag) string
 	RenderExperiences(datastructure.Experiences, []language.Tag) string

+ 1 - 0
go.mod

@@ -18,6 +18,7 @@ require (
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pquerna/otp v1.2.0
 	github.com/sirupsen/logrus v1.5.0 // indirect
+	github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086
 	github.com/stretchr/testify v1.4.0 // indirect
 	golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5
 	golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect

+ 2 - 0
go.sum

@@ -83,6 +83,8 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
 github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
+github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 h1:RYiqpb2ii2Z6J4x0wxK46kvPBbFuZcdhS+CIztmYgZs=
+github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
 github.com/sozorogami/gover v0.0.0-20171022184752-b58185e213c5 h1:TAPeDBsd52dRWoWzf5trgBzxzMYHTYjYI+4xNyCdoCU=
 github.com/sozorogami/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:nHNlDYIQZn44RvqH0kCpl/dMMVWXkav0QIgzGxV1Ab4=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=

+ 10 - 0
libamuse/qr.go

@@ -0,0 +1,10 @@
+package libamuse
+
+import (
+	"github.com/skip2/go-qrcode"
+)
+
+func ShowQr(keyUrl string) (string, error){
+	png, err := qrcode.Encode(keyUrl, qrcode.Low, 256)  // todo webp
+	return string(png), err
+}

+ 0 - 0
libamuse/serie.go


Some files were not shown because too many files changed in this diff