// Index returns people func Index(render render.Render, r doorbot.Repositories, session *auth.Authorization) { repo := r.PersonRepository() people, err := repo.All(r.DB()) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), }).Error("Api::People->Index database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } switch session.Type { case auth.AuthorizationAdministrator: render.JSON(http.StatusOK, PeopleViewModel{People: people}) case auth.AuthorizationDevice: render.JSON(http.StatusOK, PublicPeopleViewModel{People: newPublicPeople(people)}) case auth.AuthorizationPerson: if session.Person.IsAccountManager() { render.JSON(http.StatusOK, PeopleViewModel{People: people}) return } render.JSON(http.StatusOK, PublicPeopleViewModel{People: newPublicPeople(people)}) } }
// Get a specific person func Get(render render.Render, r doorbot.Repositories, params martini.Params, session *auth.Authorization) { id, err := strconv.ParseUint(params["id"], 10, 32) if err != nil { render.JSON(http.StatusBadRequest, doorbot.NewBadRequestErrorResponse([]string{"The id must be an unsigned integer"})) return } repo := r.PersonRepository() person, err := repo.Find(r.DB(), uint(id)) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "person_id": id, }).Error("Api::People->Get database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } if person == nil { err := doorbot.NewEntityNotFoundResponse([]string{"The specified person does not exists"}) render.JSON(http.StatusNotFound, err) return } switch session.Type { case auth.AuthorizationAdministrator: render.JSON(http.StatusOK, PersonViewModel{Person: person}) case auth.AuthorizationDevice: render.JSON(http.StatusOK, PublicPersonViewModel{Person: newPublicPerson(person)}) case auth.AuthorizationPerson: // Display detailed info if the requesting user is an account manager or it is the same person if session.Person.IsAccountManager() || session.Person.ID == person.ID { render.JSON(http.StatusOK, PersonViewModel{Person: person}) return } render.JSON(http.StatusOK, PublicPersonViewModel{Person: newPublicPerson(person)}) default: render.Status(http.StatusForbidden) } }
// Post creates a new person func Post(render render.Render, r doorbot.Repositories, vm PersonViewModel) { repo := r.PersonRepository() err := repo.Create(r.DB(), vm.Person) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), }).Error("Api::People->Post database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } log.WithFields(log.Fields{ "account_id": r.AccountScope(), "person_id": vm.Person.ID, }).Info("Api::People->Post person added.") render.JSON(http.StatusCreated, vm) }
// Sync doorbot users with a foreign data source using a bridge. func Sync(render render.Render, r doorbot.Repositories, b bridges.Bridges, a *doorbot.Account, session *auth.Authorization) { var bUsers []*doorbot.BridgeUser var registered []*doorbot.BridgeUser var err error personRepo := r.PersonRepository() bUserRepo := r.BridgeUserRepository() var bridgesToSync = []uint{bridges.BridgeHub, bridges.BridgeHipChat} for _, bridgeId := range bridgesToSync { f := func() bool { users, err := b.GetUsers(bridgeId) for _, u := range users { log.WithFields(log.Fields{"user": *u}).Info("User") } if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": a.ID, "bridge_id": bridgeId, }).Error("Api::People->Sync bridge error") return false } existing, err := bUserRepo.FindByBridgeID(r.DB(), bridgeId) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "step": "bridge-user-find-by-bridge-id", "bridge_id": bridgeId, }).Error("Api::People->Sync database error") return false } registered = append(registered, existing...) bUsers = append(bUsers, users...) return true } f() } tx, err := r.Transaction() if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "step": "transaction-create", }).Error("Api::People->Sync database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } var buser *doorbot.BridgeUser for _, u := range bUsers { log.WithFields(log.Fields{ "account_id": r.AccountScope(), "bridge_user_id": u.UserID, "bridge_user_email": u.Email, "bridge_user_name": u.Name, }).Debug("Api::People->Sync bridge user") buser = findRegistered(registered, u.UserID) if buser != nil { log.WithFields(log.Fields{ "account_id": r.AccountScope(), "bridge_user_id": buser.UserID, "person_id": buser.PersonID, }).Debug("Api::People->Sync registered user found") person, err := personRepo.Find(tx, buser.PersonID) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "bridge_user_id": buser.UserID, "person_id": buser.PersonID, "step": "person-find-from-bridge-id", }).Error("Api::People->Sync database error") break } person.Name = u.Name person.Email = u.Email person.PhoneNumber = u.PhoneNumber _, err = personRepo.Update(tx, person) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "bridge_user_id": buser.UserID, "person_id": buser.PersonID, "step": "person-update-from-bridge-data", }).Error("Api::People->Sync database error") break } log.WithFields(log.Fields{ "account_id": r.AccountScope(), "bridge_user_id": buser.UserID, "person_id": buser.PersonID, }).Info("Api::People->Sync person updated from bridge data") continue } log.WithFields(log.Fields{ "account_id": r.AccountScope(), "bridge_user_id": u.UserID, "bridge_user_email": u.Email, "bridge_user_name": u.Name, }).Info("Api::People->Sync new bridge user") // User does not exists. Create them args := doorbot.PersonArguments{ Name: u.Name, Email: u.Email, } person := doorbot.NewPerson(args) err = personRepo.Create(tx, person) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "bridge_user_id": buser.UserID, "step": "person-create-from-bridge-data", }).Error("Api::People->Sync database error") break } u.PersonID = person.ID err = bUserRepo.Create(tx, u) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "bridge_user_id": buser.UserID, "step": "bridge-user-create", }).Error("Api::People->Sync database error") break } continue } if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), }).Error("Api::People->Sync error") tx.Rollback() render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } err = tx.Commit() if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "step": "transaction-commit", }).Error("Api::People->Sync database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } log.WithFields(log.Fields{ "account_id": r.AccountScope(), }).Info("Api::People->Sync bridge sync completed.") render.Status(http.StatusNoContent) }
// Delete a person func Delete(render render.Render, r doorbot.Repositories, params martini.Params, a *doorbot.Account, session *auth.Authorization) { id, err := strconv.ParseUint(params["id"], 10, 32) if err != nil { render.JSON(http.StatusBadRequest, doorbot.NewBadRequestErrorResponse([]string{"The id must be an unsigned integer"})) return } var logFields log.Fields var logMessage string switch session.Type { case auth.AuthorizationAdministrator: logFields = log.Fields{ "account_id": r.AccountScope(), "person_id": id, "admnistrator_id": session.Administrator.ID, } logMessage = "Api::People->Delete user deleted by administrator" case auth.AuthorizationPerson: if !session.Person.IsAccountManager() { log.WithFields(log.Fields{ "account_id": r.AccountScope(), "person_id": id, "request_person_id": session.Person.ID, }).Warn("Api::People->Delete forbidden") render.Status(http.StatusForbidden) return } logFields = log.Fields{ "account_id": r.AccountScope(), "person_id": id, "modified_by_id": session.Person.ID, } logMessage = "Api::People->Put user deleted by user" default: log.WithFields(log.Fields{ "account_id": r.AccountScope(), "person_id": id, "request_person_id": session.Person.ID, }).Warn("Api::People->Delete forbidden") render.Status(http.StatusForbidden) return } repo := r.PersonRepository() person, err := repo.Find(r.DB(), uint(id)) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "person_id": person.ID, "step": "person-find", }).Error("Api::People->Delete database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } if person == nil { render.JSON(http.StatusNotFound, doorbot.NewEntityNotFoundResponse([]string{"The specified person does not exists"})) return } _, err = repo.Delete(r.DB(), person) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "person_id": person.ID, "step": "person-delete", }).Error("Api::People->Delete database error") render.Status(http.StatusInternalServerError) return } log.WithFields(logFields).Info(logMessage) render.Status(http.StatusNoContent) }
// Put updates a person func Put(render render.Render, r doorbot.Repositories, params martini.Params, vm PersonViewModel, session *auth.Authorization) { id, err := strconv.ParseUint(params["id"], 10, 32) if err != nil { render.JSON(http.StatusBadRequest, doorbot.NewBadRequestErrorResponse([]string{"The id must be an unsigned integer"})) return } var logFields log.Fields var logMessage string canUpdateAccountType := false switch session.Type { case auth.AuthorizationAdministrator: logFields = log.Fields{ "account_id": r.AccountScope(), "person_id": id, "admnistrator_id": session.Administrator.ID, } logMessage = "Api::People->Put user updated by administrator" canUpdateAccountType = true case auth.AuthorizationPerson: if uint(id) != session.Person.ID { if session.Person.IsAccountManager() { canUpdateAccountType = true } else { log.WithFields(log.Fields{ "account_id": r.AccountScope(), "person_id": id, "request_person_id": session.Person.ID, }).Warn("Api::People->Delete forbidden") render.Status(http.StatusForbidden) return } } logFields = log.Fields{ "account_id": r.AccountScope(), "person_id": id, "request_person_id": session.Person.ID, } logMessage = "Api::People->Put user updated by user" default: log.WithFields(log.Fields{ "account_id": r.AccountScope(), "person_id": id, "request_person_id": session.Person.ID, }).Warn("Api::People->Put forbidden") render.Status(http.StatusForbidden) return } repo := r.PersonRepository() person, err := repo.Find(r.DB(), uint(id)) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "person_id": id, "step": "person-find", }).Error("Api::People->Put database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } if person == nil { render.JSON(http.StatusNotFound, doorbot.NewEntityNotFoundResponse([]string{"The specified person does not exists"})) return } person.Name = vm.Person.Name person.Email = vm.Person.Email person.PhoneNumber = vm.Person.PhoneNumber person.Title = vm.Person.Title person.IsVisible = vm.Person.IsVisible person.IsAvailable = vm.Person.IsAvailable person.NotificationsEnabled = vm.Person.NotificationsEnabled person.NotificationsAppEnabled = vm.Person.NotificationsAppEnabled person.NotificationsChatEnabled = vm.Person.NotificationsChatEnabled person.NotificationsEmailEnabled = vm.Person.NotificationsEmailEnabled person.NotificationsSMSEnabled = vm.Person.NotificationsSMSEnabled if canUpdateAccountType { person.AccountType = vm.Person.AccountType } _, err = repo.Update(r.DB(), person) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "person_id": person.ID, "request_person_id": session.Person.ID, "step": "person-update", }).Error("Api::People->Put database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } vm.Person = person log.WithFields(logFields).Info(logMessage) render.JSON(http.StatusOK, vm) }
// Password will authenticate a person using the provided email and password. // A token will be generated if the authentication succeeds. func Password(render render.Render, account *doorbot.Account, r doorbot.Repositories, vm PasswordRequest) { personRepo := r.PersonRepository() authRepo := r.AuthenticationRepository() // Find the person by email person, err := personRepo.FindByEmail(r.DB(), vm.Authentication.Email) if err != nil { log.WithFields(log.Fields{ "error": err, "email": vm.Authentication.Email, "account_id": account.ID, "step": "find-person-by-email", }).Error("Api::Auth->Password database error") render.JSON(http.StatusUnauthorized, doorbot.NewUnauthorizedErrorResponse([]string{"Invalid email or password"})) return } if person == nil { log.WithFields(log.Fields{ "account_id": account.ID, "email": vm.Authentication.Email, "step": "find-person-by-email", }).Info("Api::Auth->Password invalid email.") render.JSON(http.StatusUnauthorized, doorbot.NewUnauthorizedErrorResponse([]string{"Invalid email or password"})) return } //Fetch the password authentication record authentication, err := authRepo.FindByPersonIDAndProviderID(r.DB(), person.ID, auth.ProviderPassword) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": account.ID, "person_id": person.ID, "step": "find-person-authentication", }).Error("Api::Auth->Password database error") render.JSON(http.StatusUnauthorized, doorbot.NewUnauthorizedErrorResponse([]string{"Invalid email or password"})) return } if authentication == nil { log.WithFields(log.Fields{ "account_id": account.ID, "person_id": person.ID, "step": "find-person-authentication", }).Info("Api::Auth->Password no authentication") render.JSON(http.StatusUnauthorized, doorbot.NewUnauthorizedErrorResponse([]string{"Invalid email or password"})) return } //Compare the passwords err = security.PasswordCompare([]byte(authentication.Token), []byte(vm.Authentication.Password)) if err != nil { log.WithFields(log.Fields{ "error": err, "person_id": person.ID, "account_id": account.ID, "step": "compare-password", }).Info("Api::Auth->Password compare password error") render.JSON(http.StatusUnauthorized, doorbot.NewUnauthorizedErrorResponse([]string{"Invalid email or password"})) return } // Find the first active API token for this person. token, err := authRepo.FindByPersonIDAndProviderID(r.DB(), person.ID, auth.ProviderAPIToken) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": account.ID, "person_id": person.ID, "step": "find-authentication", }).Error("Api::Auth->Password database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } // No active token or the person has not yet signed in. Generate a new token. if token == nil { token = &doorbot.Authentication{ PersonID: person.ID, ProviderID: auth.ProviderAPIToken, Token: security.GenerateAPIToken(), } err = authRepo.Create(r.DB(), token) if err != nil { log.WithFields(log.Fields{ "error": err, "person_id": person.ID, "account_id": account.ID, "step": "save-authentication", }).Error("Api::Auth->Password database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } } log.WithFields(log.Fields{ "account_id": account.ID, "person_id": person.ID, }).Info("Api::Auth->Password user logged in") resp := APITokenResponse{} resp.Authentication.Token = fmt.Sprintf("%d.%s", person.ID, token.Token) resp.Person = person resp.Policy = getPolicyForPerson(person) render.JSON(http.StatusOK, resp) }
func Token(render render.Render, account *doorbot.Account, r doorbot.Repositories, vm TokenRequest) { var person *doorbot.Person var device *doorbot.Device var policy *security.Policy switch vm.Authentication.Type { case auth.AuthorizationPerson: authRepo := r.AuthenticationRepository() personRepo := r.PersonRepository() authentication, err := authRepo.FindByProviderIDAndToken(r.DB(), auth.ProviderAPIToken, vm.Authentication.Token) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": account.ID, "token": vm.Authentication.Token, "step": "find-token", }).Error("Api::Auth->Token database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } if authentication == nil { log.WithFields(log.Fields{ "account_id": account.ID, "token": vm.Authentication.Token, "step": "find-token", }).Info("Api::Auth->Token no token found.") render.JSON(http.StatusUnauthorized, doorbot.NewUnauthorizedErrorResponse([]string{"Invalid token"})) return } person, err = personRepo.Find(r.DB(), authentication.PersonID) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": account.ID, "token": vm.Authentication.Token, "step": "find-person", }).Error("Api::Auth->Token database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } if person == nil { log.WithFields(log.Fields{ "account_id": account.ID, "token": vm.Authentication.Token, "step": "find-device", }).Warning("Api::Auth->Token person not found.") render.JSON(http.StatusUnauthorized, doorbot.NewUnauthorizedErrorResponse([]string{"Invalid token"})) return } policy = getPolicyForPerson(person) break case auth.AuthorizationDevice: deviceRepo := r.DeviceRepository() device, err := deviceRepo.FindByToken(r.DB(), vm.Authentication.Token) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": account.ID, "token": vm.Authentication.Token, "step": "find-device", }).Error("Api::Auth->Token database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } if device == nil { log.WithFields(log.Fields{ "account_id": account.ID, "token": vm.Authentication.Token, "step": "find-device", }).Info("Api::Auth->Token no device found.") render.JSON(http.StatusUnauthorized, doorbot.NewUnauthorizedErrorResponse([]string{"Invalid token"})) return } } resp := APITokenResponse{} resp.Authentication.Token = vm.Authentication.Token resp.Person = person resp.Policy = policy resp.Device = device render.JSON(http.StatusOK, resp) }
// Notify someone their presence is needed at a given door. func Notify(render render.Render, account *doorbot.Account, r doorbot.Repositories, notificator notifications.Notificator, vm ViewModel) { notification := vm.Notification log.WithFields(log.Fields{ "account_id": account.ID, "person_id": notification.PersonID, "door_id": notification.DoorID, }).Info("Api::Notifications->Notify request") peopleRepo := r.PersonRepository() doorRepo := r.DoorRepository() person, err := peopleRepo.Find(r.DB(), notification.PersonID) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": account.ID, "person_id": notification.PersonID, "door_id": notification.DoorID, "step": "person-find", }).Error("Api::Notifications->Notify database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } if person == nil { render.JSON(http.StatusNotFound, doorbot.NewEntityNotFoundResponse([]string{"The specified person does not exists."})) log.WithFields(log.Fields{ "account_id": account.ID, "person_id": notification.PersonID, "door_id": notification.DoorID, }).Info("Api::Notifications->Notify person not found") return } if !person.IsVisible || !person.IsAvailable { log.WithFields(log.Fields{ "account_id": account.ID, "person_id": notification.PersonID, "door_id": notification.DoorID, "person_is_visible": person.IsVisible, "person_is_available": person.IsAvailable, }).Info("Api::Notifications->Notify person is not available/visible") //TODO Would there be a better status code? render.JSON(http.StatusForbidden, doorbot.NewForbiddenErrorResponse([]string{"The specified user is currently not available."})) return } //TODO Infer from the device token? door, err := doorRepo.Find(r.DB(), notification.DoorID) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": account.ID, "person_id": notification.PersonID, "door_id": notification.DoorID, "step": "door-find", }).Error("Api::Notifications->Notify database error") render.JSON(http.StatusInternalServerError, doorbot.NewInternalServerErrorResponse([]string{})) return } if door == nil { render.JSON(http.StatusNotFound, doorbot.NewEntityNotFoundResponse([]string{"The specified door does not exists."})) log.WithFields(log.Fields{ "account_id": account.ID, "person_id": person.ID, "door_id": notification.DoorID, }).Info("Api::Notifications->Notify door not found") return } notificator.KnockKnock(door, person) render.JSON(http.StatusAccepted, ViewModel{Notification: notification}) }
// AuthenticatePerson wraps the authentication logic for people func AuthenticatePerson(r doorbot.Repositories, token string) (*doorbot.Person, error) { ar := r.AuthenticationRepository() pr := r.PersonRepository() parts := strings.Split(token, ".") if len(parts) != 2 { log.WithFields(log.Fields{ "account_id": r.AccountScope(), "token": token, "parts_count": len(parts), }).Warn("Auth->AuthenticatePerson Invalid person token") return nil, nil } id, err := strconv.ParseUint(parts[0], 10, 32) if err != nil { return nil, nil } token = parts[1] authentication, err := ar.FindByProviderIDAndPersonIDAndToken(r.DB(), ProviderAPIToken, uint(id), token) if err != nil { log.WithFields(log.Fields{ "error": err, "step": "authentication-get-by-person-and-token", "person_id": id, "account_id": r.AccountScope(), "token": token, }).Error("Auth->AuthenticatePerson database error") return nil, err } if authentication == nil { log.WithFields(log.Fields{ "token": token, "person_id": id, "account_id": r.AccountScope(), }).Info("Auth->AuthenticatePerson authentication not found") return nil, nil } person, err := pr.Find(r.DB(), authentication.PersonID) if err != nil { log.WithFields(log.Fields{ "error": err, "account_id": r.AccountScope(), "person_id": id, "authentication_id": authentication.ID, "step": "person-find", }).Error("Auth->AuthenticatePerson database error") return nil, err } if person == nil { log.WithFields(log.Fields{ "token": token, "person_id": id, "account_id": r.AccountScope(), "authentication_id": authentication.ID, }).Error("Auth->AuthenticatePerson person not found") return nil, nil } return person, err }
// 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}) }