// AddOrganizationOwner It is handler for POST /organizations/{globalid}/owners func (api OrganizationsAPI) AddOrganizationOwner(w http.ResponseWriter, r *http.Request) { globalid := mux.Vars(r)["globalid"] var s searchMember if err := json.NewDecoder(r.Body).Decode(&s); err != nil { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } orgMgr := organization.NewManager(r) org, err := orgMgr.GetByName(globalid) if err != nil { if err == mgo.ErrNotFound { http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) } else { handleServerError(w, "getting organization", err) } return } u, err := SearchUser(r, s.SearchString) if err != nil { log.Error(err) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return } for _, membername := range org.Owners { if membername == u.Username { http.Error(w, http.StatusText(http.StatusConflict), http.StatusConflict) return } } // Create JoinRequest invitationMgr := invitations.NewInvitationManager(r) orgReq := &invitations.JoinOrganizationInvitation{ Role: invitations.RoleOwner, Organization: globalid, User: u.Username, Status: invitations.RequestPending, Created: db.DateTime(time.Now()), } if err := invitationMgr.Save(orgReq); err != nil { log.Error("Error inviting owner: ", err.Error()) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(orgReq) }
//ProcessRegistrationForm processes the user registration form func (service *Service) ProcessRegistrationForm(w http.ResponseWriter, request *http.Request) { response := struct { Redirecturl string `json:"redirecturl"` Error string `json:"error"` }{} values := struct { TwoFAMethod string `json:"twofamethod"` Login string `json:"login"` Email string `json:"email"` Phonenumber string `json:"phonenumber"` TotpCode string `json:"totpcode"` Password string `json:"password"` RedirectParams string `json:"redirectparams"` }{} if err := json.NewDecoder(request.Body).Decode(&values); err != nil { log.Debug("Error decoding the registration request:", err) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } twoFAMethod := values.TwoFAMethod if twoFAMethod != "sms" && twoFAMethod != "totp" { log.Info("Invalid 2fa method during registration: ", twoFAMethod) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } totpsession, err := service.GetSession(request, SessionForRegistration, "totp") if err != nil { log.Error("ERROR while getting the totp registration session", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } if totpsession.IsNew { log.Debug("New registration session while processing the registration form") http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) return } totpsecret, ok := totpsession.Values["secret"].(string) if !ok { log.Error("Unable to convert the stored session totp secret to a string") http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } valid := user.ValidateUsername(values.Login) var phonenumber user.Phonenumber if !valid { response.Error = "invalid_username_format" w.WriteHeader(422) json.NewEncoder(w).Encode(response) return } newuser := &user.User{ Username: values.Login, EmailAddresses: []user.EmailAddress{user.EmailAddress{Label: "main", EmailAddress: values.Email}}, } //validate the username is not taken yet userMgr := user.NewManager(request) count, err := userMgr.GetPendingRegistrationsCount() if err != nil { log.Error("Failed to get pending registerations count: ", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } log.Debug("count", count) if count >= MAX_PENDING_REGISTRATION_COUNT { log.Warn("Maximum amount of pending registrations reached") http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } //we now just depend on mongo unique index to avoid duplicates when concurrent requests are made userExists, err := userMgr.Exists(newuser.Username) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } if userExists { log.Debug("USER ", newuser.Username, " already registered") http.Error(w, http.StatusText(http.StatusConflict), http.StatusConflict) return } if twoFAMethod == "sms" { phonenumber = user.Phonenumber{Label: "main", Phonenumber: values.Phonenumber} if !phonenumber.IsValid() { log.Debug("Invalid phone number") w.WriteHeader(422) response.Error = "invalid_phonenumber" json.NewEncoder(w).Encode(&response) return } newuser.Phonenumbers = []user.Phonenumber{phonenumber} // Remove account after 3 days if it still doesn't have a verified phone by then duration := time.Duration(time.Hour * 24 * 3) expiresAt := time.Now() expiresAt = expiresAt.Add(duration) newuser.Expire = db.DateTime(expiresAt) } else { token := totp.TokenFromSecret(totpsecret) if !token.Validate(values.TotpCode) { log.Debug("Invalid totp code") w.WriteHeader(422) response.Error = "invalid_totpcode" json.NewEncoder(w).Encode(&response) return } } userMgr.Save(newuser) passwdMgr := password.NewManager(request) err = passwdMgr.Save(newuser.Username, values.Password) if err != nil { log.Error(err) if err.Error() != "internal_error" { w.WriteHeader(422) response.Error = "invalid_password" json.NewEncoder(w).Encode(&response) } else { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } return } if twoFAMethod == "sms" { validationkey, err := service.phonenumberValidationService.RequestValidation(request, newuser.Username, phonenumber, fmt.Sprintf("https://%s/phonevalidation", request.Host)) if err != nil { log.Error(err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } registrationSession, err := service.GetSession(request, SessionForRegistration, "registrationdetails") registrationSession.Values["username"] = newuser.Username registrationSession.Values["phonenumbervalidationkey"] = validationkey registrationSession.Values["redirectparams"] = values.RedirectParams sessions.Save(request, w) response.Redirecturl = fmt.Sprintf("https://%s/register#/smsconfirmation", request.Host) json.NewEncoder(w).Encode(&response) return } totpMgr := totp.NewManager(request) totpMgr.Save(newuser.Username, totpsecret) log.Debugf("Registered %s", newuser.Username) service.loginUser(w, request, newuser.Username) }
// AddOrganizationMember Assign a member to organization // It is handler for POST /organizations/{globalid}/members func (api OrganizationsAPI) AddOrganizationMember(w http.ResponseWriter, r *http.Request) { globalid := mux.Vars(r)["globalid"] var s searchMember if err := json.NewDecoder(r.Body).Decode(&s); err != nil { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } orgMgr := organization.NewManager(r) org, err := orgMgr.GetByName(globalid) if err != nil { if err == mgo.ErrNotFound { http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) } else { handleServerError(w, "getting organization", err) } return } // Check if user exists u, err := SearchUser(r, s.SearchString) if err != nil { if err != mgo.ErrNotFound { log.Error(err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return } for _, membername := range org.Members { if membername == u.Username { http.Error(w, http.StatusText(http.StatusConflict), http.StatusConflict) return } } for _, membername := range org.Owners { if membername == u.Username { http.Error(w, http.StatusText(http.StatusConflict), http.StatusConflict) return } } // Create JoinRequest invitationMgr := invitations.NewInvitationManager(r) count, err := invitationMgr.CountByOrganization(globalid) if err != nil { log.Error(err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } if count >= MAX_AMOUNT_INVITATIONS_PER_ORGANIZATION { log.Error("Reached invitation limit for organization ", globalid) writeErrorResponse(w, 422, "max_amount_of_invitations_reached") return } orgReq := &invitations.JoinOrganizationInvitation{ Role: invitations.RoleMember, Organization: globalid, User: u.Username, Status: invitations.RequestPending, Created: db.DateTime(time.Now()), } if err := invitationMgr.Save(orgReq); err != nil { log.Error("Error inviting member: ", err.Error()) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(orgReq) }