Beispiel #1
0
// 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)
}
Beispiel #2
0
// 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
}
Beispiel #3
0
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)
}
Beispiel #4
0
// 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})
}