// generateHost generates a host that isn't yet taken. func generateHost(r doorbot.Repositories, config *doorbot.DoorbotConfig) (string, error) { repo := r.AccountRepository() // Generate a random valid host name for i := 0; i < 10; i++ { tmp := strings.ToLower(security.RandomPassword(10)) exists, err := repo.FindByHost(r.DB(), tmp) if err != nil { log.WithFields(log.Fields{ "error": err, "host": tmp, }).Error("Api::Accounts->Register database error.") return "", err } if exists == nil { return tmp, nil } } return "", nil }
// Register a new account ( used by the dashboard ) func Register(render render.Render, config *doorbot.DoorbotConfig, r doorbot.Repositories, n notifications.Notificator, data RegisterViewModel) { repo := r.AccountRepository() tx, err := r.Transaction() if err != nil { log.WithFields(log.Fields{ "error": err, "step": "transaction-create", }).Error("Api::Accounts->Register database error.") render.Status(http.StatusInternalServerError) return } var host string if len(data.Account.Host) == 0 { host, err = generateHost(r, config) } else { var account *doorbot.Account account, err = repo.FindByHost(r.DB(), data.Account.Host) if account != nil { tx.Rollback() render.Status(http.StatusConflict) return } host = data.Account.Host } if err != nil { tx.Rollback() render.Status(http.StatusInternalServerError) return } if len(host) == 0 { log.WithFields(log.Fields{ "host": host, "step": "host-generation", }).Error("Api::Accounts->Register Unable to set a hostname.") tx.Rollback() render.Status(http.StatusServiceUnavailable) return } // Create the account instance account := &doorbot.Account{ Name: data.Account.Name, ContactName: data.Account.ContactName, ContactEmail: data.Account.ContactEmail, //TODO append the doorbot production domain Host: host, IsEnabled: true, } err = repo.Create(tx, account) if err != nil { tx.Rollback() log.WithFields(log.Fields{ "error": err, "host": host, "step": "account-create", }).Error("Api::Accounts->Register database error.") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } // Update the repositories account scopes to the one we just created. r.SetAccountScope(account.ID) // We need to create a person with a password to let them log in on the dashboard. person := &doorbot.Person{ Name: data.Account.ContactName, Email: data.Account.ContactEmail, AccountType: doorbot.AccountOwner, } ar := r.PersonRepository() err = ar.Create(tx, person) if err != nil { log.WithFields(log.Fields{ "error": err, "host": host, "email": person.Email, "step": "person-create", }).Error("Api::Accounts->Register database error.") tx.Rollback() render.Status(http.StatusInternalServerError) return } // Generate a random password password := security.RandomPassword(8) hash, err := security.PasswordCrypt([]byte(password)) if err != nil { log.WithFields(log.Fields{ "error": err, "host": host, "email": person.Email, "step": "person-password", }).Error("Api::Accounts->Register password generation error.") tx.Rollback() render.Status(http.StatusInternalServerError) return } // Create a new authentication record for the user authr := r.AuthenticationRepository() authentication := &doorbot.Authentication{ PersonID: person.ID, ProviderID: auth.ProviderPassword, Token: string(hash), } err = authr.Create(tx, authentication) if err != nil { log.WithFields(log.Fields{ "error": err, "host": host, "email": person.Email, "step": "person-authentication", }).Error("Api::Accounts->Register database error.") tx.Rollback() render.Status(http.StatusInternalServerError) return } err = tx.Commit() if err != nil { log.WithFields(log.Fields{ "error": err, "host": host, "email": person.Email, "step": "transaction-commit", }).Error("Api::Accounts->Register database error.") tx.Rollback() render.Status(http.StatusInternalServerError) return } //TODO Send an email to the user. log.WithFields(log.Fields{ "account_id": account.ID, "account_host": account.Host, "person_id": person.ID, }).Info("Account created") n.AccountCreated(account, person, password) render.JSON(http.StatusCreated, AccountViewModel{Account: account}) }