Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions db/default_crud.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,22 @@ func (d *Default) FindBy(key string, value interface{}) (*gorethink.Cursor, erro
return cursor, nil
}

func (d *Default) FindByAndCount(key string, value interface{}) (int, error) {
cursor, err := d.GetTable().Filter(map[string]interface{}{
key: value,
}).Count().Run(d.session)
if err != nil {
return 0, err
}

var count int
if err := cursor.One(&count); err != nil {
return 0, NewDatabaseError(d, err, "")
}

return count, nil
}

// FindByAndFetch retrieves a value by key and then fills results with the result.
func (d *Default) FindByAndFetch(key string, value interface{}, results interface{}) error {
cursor, err := d.FindBy(key, value)
Expand Down
1 change: 1 addition & 0 deletions db/rethink_crud.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type RethinkReader interface {
FindBy(key string, value interface{}) (*rethink.Cursor, error)
FindByAndFetch(key string, value interface{}, results interface{}) error
FindByAndFetchOne(key string, value interface{}, result interface{}) error
FindByAndCount(key string, value interface{}) (int, error)

Where(filter map[string]interface{}) (*rethink.Cursor, error)
WhereAndFetch(filter map[string]interface{}, results interface{}) error
Expand Down
17 changes: 9 additions & 8 deletions db/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ var (

// Indexes of tables in the database
var tableIndexes = map[string][]string{
"tokens": []string{"user", "user_id"},
"accounts": []string{"name"},
"emails": []string{"user_id"},
"drafts": []string{"user_id"},
"contacts": []string{},
"threads": []string{"user_id"},
"labels": []string{},
"keys": []string{},
"tokens": []string{"user", "user_id"},
"accounts": []string{"name"},
"emails": []string{"user_id"},
"drafts": []string{"user_id"},
"contacts": []string{},
"threads": []string{"user_id"},
"labels": []string{},
"keys": []string{},
"reservations": []string{},
}

// List of names of databases
Expand Down
26 changes: 26 additions & 0 deletions db/table_accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,29 @@ func (a *AccountsTable) GetTokenOwner(token *models.Token) (*models.Account, err

return user, nil
}

func (a *AccountsTable) IsUsernameUsed(name string) (bool, error) {
count, err := a.FindByAndCount("name", name)
if err != nil {
return false, err
}

if count == 0 {
return false, nil
}

return true, nil
}

func (a *AccountsTable) IsEmailUsed(email string) (bool, error) {
count, err := a.FindByAndCount("alt_email", email)
if err != nil {
return false, err
}

if count == 0 {
return false, nil
}

return true, nil
}
32 changes: 32 additions & 0 deletions db/table_reservations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package db

// ReservationsTable is a CRUD interface for accessing the "reservation" table
type ReservationsTable struct {
RethinkCRUD
}

func (r *ReservationsTable) IsUsernameUsed(name string) (bool, error) {
count, err := r.FindByAndCount("name", name)
if err != nil {
return false, err
}

if count == 0 {
return false, nil
}

return true, nil
}

func (r *ReservationsTable) IsEmailUsed(email string) (bool, error) {
count, err := r.FindByAndCount("email", email)
if err != nil {
return false, err
}

if count == 0 {
return false, nil
}

return true, nil
}
1 change: 1 addition & 0 deletions env/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type Flags struct {

SessionDuration int
ClassicRegistration bool
UsernameReservation bool

RethinkDBURL string
RethinkDBKey string
Expand Down
2 changes: 2 additions & 0 deletions env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ var (
Keys *db.KeysTable
// Contacts is the global instance of ContactsTable
Contacts *db.ContactsTable
// Reservations is the global instance of ReservationsTable
Reservations *db.ReservationsTable
)
6 changes: 4 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ var (
bindAddress = flag.String("bind", ":5000", "Network address used to bind")
apiVersion = flag.String("version", "v0", "Shown API version")
logFormatterType = flag.String("log", "text", "Log formatter type. Either \"json\" or \"text\"")
sessionDuration = flag.Int("session_duration", 72, "Session duration expressed in hours")
forceColors = flag.Bool("force_colors", false, "Force colored prompt?")
// Registration settings
sessionDuration = flag.Int("session_duration", 72, "Session duration expressed in hours")
classicRegistration = flag.Bool("classic_registration", false, "Classic registration enabled?")
usernameReservation = flag.Bool("username_reservation", false, "Username reservation enabled?")
// Database-related flags
rethinkdbURL = flag.String("rethinkdb_url", func() string {
address := os.Getenv("RETHINKDB_PORT_28015_TCP_ADDR")
Expand Down Expand Up @@ -55,8 +56,9 @@ func main() {
LogFormatterType: *logFormatterType,
ForceColors: *forceColors,

ClassicRegistration: *classicRegistration,
SessionDuration: *sessionDuration,
ClassicRegistration: *classicRegistration,
UsernameReservation: *usernameReservation,

RethinkDBURL: *rethinkdbURL,
RethinkDBKey: *rethinkdbKey,
Expand Down
8 changes: 8 additions & 0 deletions models/reservation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package models

// Reservation is a closed beta account request
type Reservation struct {
Resource

Email string `json:"email" gorethink:"email"`
}
76 changes: 69 additions & 7 deletions routes/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,16 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) {
// 1) username + token + password - invite
// 2) username + password + alt_email - register with confirmation
// 3) alt_email only - register for beta (add to queue)
// 4) alt_email + username - register for beta with username reservation
requestType := "unknown"
if input.AltEmail == "" && input.Username != "" && input.Password != "" && input.Token != "" {
requestType = "invited"
} else if input.AltEmail != "" && input.Username != "" && input.Password != "" && input.Token == "" {
requestType = "classic"
} else if input.AltEmail != "" && input.Username == "" && input.Password == "" && input.Token == "" {
requestType = "queue"
requestType = "queue/classic"
} else if input.AltEmail != "" && input.Username != "" && input.Password == "" && input.Token == "" {
requestType = "queue/reserve"
}

// "unknown" requests are empty and invalid
Expand All @@ -79,12 +82,70 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) {
return
}

// Adding to queue will be implemented soon
if requestType == "queue" {
// Implementation awaits https://trello.com/c/SLM0qK1O/91-account-registration-queue
utils.JSONResponse(w, 501, &AccountsCreateResponse{
Success: false,
Message: "Sorry, not implemented yet",
// Adding to [beta] queue
if requestType[:5] == "queue" {
if requestType[6:] == "reserve" {
// Is username reservation enabled?
if !env.Config.UsernameReservation {
utils.JSONResponse(w, 403, &AccountsCreateResponse{
Success: false,
Message: "Username reservation is disabled",
})
return
}

if used, err := env.Reservations.IsUsernameUsed(input.Username); err != nil || used {
utils.JSONResponse(w, 400, &AccountsCreateResponse{
Success: false,
Message: "Username already reserved",
})
return
}

if used, err := env.Accounts.IsUsernameUsed(input.Username); err != nil || used {
utils.JSONResponse(w, 400, &AccountsCreateResponse{
Success: false,
Message: "Username already used",
})
return
}
}

// Ensure that the email is not already used to reserve/register
if used, err := env.Reservations.IsEmailUsed(input.AltEmail); err != nil || used {
utils.JSONResponse(w, 400, &AccountsCreateResponse{
Success: false,
Message: "Email already used for a reservation",
})
return
}

if used, err := env.Accounts.IsEmailUsed(input.AltEmail); err != nil || used {
utils.JSONResponse(w, 400, &AccountsCreateResponse{
Success: false,
Message: "Email already used for a reservation",
})
return
}

// Prepare data to insert
reservation := &models.Reservation{
Email: input.AltEmail,
Resource: models.MakeResource("", input.Username),
}

err := env.Reservations.Insert(reservation)
if err != nil {
utils.JSONResponse(w, 500, &AccountsCreateResponse{
Success: false,
Message: "Internal error while reserving the account",
})
return
}

utils.JSONResponse(w, 201, &AccountsCreateResponse{
Success: true,
Message: "Reserved an account",
})
return
}
Expand Down Expand Up @@ -148,6 +209,7 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) {
account := &models.Account{
Resource: models.MakeResource("", input.Username),
Type: "beta",
AltEmail: input.AltEmail,
}

// Set the password
Expand Down
Loading