github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/documize/api/endpoint/user_endpoint.go (about)

     1  // Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
     2  //
     3  // This software (Documize Community Edition) is licensed under
     4  // GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
     5  //
     6  // You can operate outside the AGPL restrictions by purchasing
     7  // Documize Enterprise Edition and obtaining a commercial license
     8  // by contacting <sales@documize.com>.
     9  //
    10  // https://documize.com
    11  
    12  package endpoint
    13  
    14  import (
    15  	"database/sql"
    16  	"encoding/json"
    17  	"fmt"
    18  	"io/ioutil"
    19  	"net/http"
    20  	"net/url"
    21  	"strings"
    22  
    23  	"github.com/documize/community/documize/api/entity"
    24  	"github.com/documize/community/documize/api/mail"
    25  	"github.com/documize/community/documize/api/request"
    26  	"github.com/documize/community/documize/api/util"
    27  	"github.com/documize/community/wordsmith/api"
    28  	"github.com/documize/community/wordsmith/log"
    29  	"github.com/documize/community/wordsmith/utility"
    30  
    31  	"github.com/gorilla/mux"
    32  )
    33  
    34  // AddUser is the endpoint that enables an administrator to add a new user for their orgaisation.
    35  func AddUser(w http.ResponseWriter, r *http.Request) {
    36  	method := "AddUser"
    37  	p := request.GetPersister(r)
    38  
    39  	if !p.Context.Administrator {
    40  		writeForbiddenError(w)
    41  		return
    42  	}
    43  
    44  	defer utility.Close(r.Body)
    45  	body, err := ioutil.ReadAll(r.Body)
    46  
    47  	if err != nil {
    48  		writePayloadError(w, method, err)
    49  		return
    50  	}
    51  
    52  	userModel := entity.User{}
    53  	err = json.Unmarshal(body, &userModel)
    54  
    55  	if err != nil {
    56  		writeJSONMarshalError(w, method, "user", err)
    57  		return
    58  	}
    59  
    60  	// data validation
    61  	userModel.Email = strings.ToLower(strings.TrimSpace(userModel.Email))
    62  	userModel.Firstname = strings.TrimSpace(userModel.Firstname)
    63  	userModel.Lastname = strings.TrimSpace(userModel.Lastname)
    64  	userModel.Password = strings.TrimSpace(userModel.Password)
    65  
    66  	if len(userModel.Email) == 0 {
    67  		writeBadRequestError(w, method, "Missing email")
    68  		return
    69  	}
    70  
    71  	if len(userModel.Firstname) == 0 {
    72  		writeBadRequestError(w, method, "Missing firstname")
    73  		return
    74  	}
    75  
    76  	if len(userModel.Lastname) == 0 {
    77  		writeBadRequestError(w, method, "Missing lastname")
    78  		return
    79  	}
    80  
    81  	userModel.Initials = utility.MakeInitials(userModel.Firstname, userModel.Lastname)
    82  
    83  	// generate secrets
    84  	requestedPassword := util.GenerateRandomPassword()
    85  	userModel.Salt = util.GenerateSalt()
    86  	userModel.Password = util.GeneratePassword(requestedPassword, userModel.Salt)
    87  
    88  	// only create account if not dupe
    89  	addUser := true
    90  	addAccount := true
    91  	var userID string
    92  
    93  	userDupe, err := p.GetUserByEmail(userModel.Email)
    94  
    95  	if err != nil && err != sql.ErrNoRows {
    96  		writeGeneralSQLError(w, method, err)
    97  		return
    98  	}
    99  
   100  	if userModel.Email == userDupe.Email {
   101  		addUser = false
   102  		userID = userDupe.RefID
   103  
   104  		log.Info("Dupe user found, will not add")
   105  	}
   106  
   107  	tx, err := request.Db.Beginx()
   108  
   109  	if err != nil {
   110  		writeTransactionError(w, method, err)
   111  		return
   112  	}
   113  
   114  	p.Context.Transaction = tx
   115  
   116  	if addUser {
   117  		userID = util.UniqueID()
   118  		userModel.RefID = userID
   119  		err = p.AddUser(userModel)
   120  
   121  		if err != nil {
   122  			log.IfErr(tx.Rollback())
   123  			writeGeneralSQLError(w, method, err)
   124  			return
   125  		}
   126  
   127  		log.Info("Adding user")
   128  	} else {
   129  		attachUserAccounts(p, p.Context.OrgID, &userDupe)
   130  
   131  		for _, a := range userDupe.Accounts {
   132  			if a.OrgID == p.Context.OrgID {
   133  				addAccount = false
   134  				log.Info("Dupe account found, will not add")
   135  				break
   136  			}
   137  		}
   138  	}
   139  
   140  	// set up user account for the org
   141  	if addAccount {
   142  		var a entity.Account
   143  		a.UserID = userID
   144  		a.OrgID = p.Context.OrgID
   145  		a.Editor = true
   146  		a.Admin = false
   147  		accountID := util.UniqueID()
   148  		a.RefID = accountID
   149  
   150  		err = p.AddAccount(a)
   151  
   152  		if err != nil {
   153  			log.IfErr(tx.Rollback())
   154  			writeGeneralSQLError(w, method, err)
   155  			return
   156  		}
   157  	}
   158  
   159  	log.IfErr(tx.Commit())
   160  
   161  	// If we did not add user or give them access (account) then we error back
   162  	if !addUser && !addAccount {
   163  		writeDuplicateError(w, method, "user")
   164  		return
   165  	}
   166  
   167  	// Invite new user
   168  	inviter, err := p.GetUser(p.Context.UserID)
   169  	log.IfErr(err)
   170  
   171  	// Prepare invitation email (that contains SSO link)
   172  	if addUser && addAccount {
   173  		size := len(requestedPassword)
   174  		auth := fmt.Sprintf("%s:%s:%s", p.Context.AppURL, userModel.Email, requestedPassword[:size])
   175  		encrypted := utility.EncodeBase64([]byte(auth))
   176  
   177  		url := fmt.Sprintf("%s/%s", getAppURL(p.Context, "auth/sso"), url.QueryEscape(string(encrypted)))
   178  		go mail.InviteNewUser(userModel.Email, inviter.Fullname(), url, userModel.Email, requestedPassword)
   179  
   180  		log.Info(fmt.Sprintf("%s invited by %s on %s", userModel.Email, inviter.Email, p.Context.AppURL))
   181  
   182  	} else {
   183  		go mail.InviteExistingUser(userModel.Email, inviter.Fullname(), getAppURL(p.Context, ""))
   184  
   185  		log.Info(fmt.Sprintf("%s is giving access to an existing user %s", inviter.Email, userModel.Email))
   186  	}
   187  
   188  	// Send back new user record
   189  	userModel, err = getSecuredUser(p, p.Context.OrgID, userID)
   190  
   191  	json, err := json.Marshal(userModel)
   192  
   193  	if err != nil {
   194  		writeJSONMarshalError(w, method, "user", err)
   195  		return
   196  	}
   197  
   198  	writeSuccessBytes(w, json)
   199  }
   200  
   201  // GetOrganizationUsers is the endpoint that allows administrators to view the users in their organisation.
   202  func GetOrganizationUsers(w http.ResponseWriter, r *http.Request) {
   203  	method := "GetUsersForOrganization"
   204  	p := request.GetPersister(r)
   205  
   206  	if !p.Context.Editor && !p.Context.Administrator {
   207  		writeForbiddenError(w)
   208  		return
   209  	}
   210  
   211  	users, err := p.GetUsersForOrganization()
   212  
   213  	if err != nil && err != sql.ErrNoRows {
   214  		writeServerError(w, method, err)
   215  		return
   216  	}
   217  
   218  	for i := range users {
   219  		attachUserAccounts(p, p.Context.OrgID, &users[i])
   220  	}
   221  
   222  	json, err := json.Marshal(users)
   223  
   224  	if err != nil {
   225  		writeJSONMarshalError(w, method, "user", err)
   226  		return
   227  	}
   228  
   229  	writeSuccessBytes(w, json)
   230  }
   231  
   232  // GetFolderUsers returns every user within a given space
   233  func GetFolderUsers(w http.ResponseWriter, r *http.Request) {
   234  	method := "GetUsersForSpace"
   235  	p := request.GetPersister(r)
   236  	var users []entity.User
   237  	var err error
   238  
   239  	params := mux.Vars(r)
   240  	folderID := params["folderID"]
   241  
   242  	if len(folderID) == 0 {
   243  		writeBadRequestError(w, method, "missing folderID")
   244  		return
   245  	}
   246  
   247  	// check to see folder type as it determines user selection criteria
   248  	folder, err := p.GetLabel(folderID)
   249  
   250  	if err != nil && err != sql.ErrNoRows {
   251  		log.Error(fmt.Sprintf("%s: cannot fetch space %s", method, folderID), err)
   252  		writeUsers(w, nil)
   253  		return
   254  	}
   255  
   256  	switch folder.Type {
   257  	case entity.FolderTypePublic:
   258  		// return all users for team
   259  		users, err = p.GetUsersForOrganization()
   260  		break
   261  	case entity.FolderTypePrivate:
   262  		// just me
   263  		var user entity.User
   264  		user, err = p.GetUser(p.Context.UserID)
   265  		users = append(users, user)
   266  		break
   267  	case entity.FolderTypeRestricted:
   268  		users, err = p.GetFolderUsers(folderID)
   269  		break
   270  	}
   271  
   272  	if err != nil && err != sql.ErrNoRows {
   273  		log.Error(fmt.Sprintf("%s: cannot fetch users for space %s", method, folderID), err)
   274  		writeUsers(w, nil)
   275  		return
   276  	}
   277  
   278  	writeUsers(w, users)
   279  }
   280  
   281  // GetUser returns user specified by Id
   282  func GetUser(w http.ResponseWriter, r *http.Request) {
   283  	method := "GetUser"
   284  	p := request.GetPersister(r)
   285  
   286  	params := mux.Vars(r)
   287  	userID := params["userID"]
   288  
   289  	if len(userID) == 0 {
   290  		writeMissingDataError(w, method, "userId")
   291  		return
   292  	}
   293  
   294  	if userID != p.Context.UserID {
   295  		writeBadRequestError(w, method, "User Id mismatch")
   296  		return
   297  	}
   298  
   299  	user, err := getSecuredUser(p, p.Context.OrgID, userID)
   300  
   301  	if err == sql.ErrNoRows {
   302  		writeNotFoundError(w, method, userID)
   303  		return
   304  	}
   305  
   306  	if err != nil {
   307  		writeServerError(w, method, err)
   308  		return
   309  	}
   310  
   311  	json, err := json.Marshal(user)
   312  
   313  	if err != nil {
   314  		writeJSONMarshalError(w, method, "user", err)
   315  		return
   316  	}
   317  
   318  	writeSuccessBytes(w, json)
   319  }
   320  
   321  // DeleteUser is the endpoint to delete a user specified by userID, the caller must be an Administrator.
   322  func DeleteUser(w http.ResponseWriter, r *http.Request) {
   323  	method := "DeleteUser"
   324  	p := request.GetPersister(r)
   325  
   326  	if !p.Context.Administrator {
   327  		writeForbiddenError(w)
   328  		return
   329  	}
   330  
   331  	params := mux.Vars(r)
   332  	userID := params["userID"]
   333  
   334  	if len(userID) == 0 {
   335  		writeMissingDataError(w, method, "userID")
   336  		return
   337  	}
   338  
   339  	if userID == p.Context.UserID {
   340  		writeBadRequestError(w, method, "cannot delete self")
   341  		return
   342  	}
   343  
   344  	tx, err := request.Db.Beginx()
   345  
   346  	if err != nil {
   347  		writeTransactionError(w, method, err)
   348  		return
   349  	}
   350  
   351  	p.Context.Transaction = tx
   352  
   353  	err = p.DeactiveUser(userID)
   354  
   355  	if err != nil {
   356  		log.IfErr(tx.Rollback())
   357  		writeGeneralSQLError(w, method, err)
   358  		return
   359  	}
   360  
   361  	err = p.ChangeLabelOwner(userID, p.Context.UserID)
   362  	log.IfErr(err)
   363  
   364  	log.IfErr(tx.Commit())
   365  
   366  	writeSuccessString(w, "{}")
   367  }
   368  
   369  // UpdateUser is the endpoint to update user information for the given userID.
   370  // Note that unless they have admin privildges, a user can only update their own information.
   371  // Also, only admins can update user roles in organisations.
   372  func UpdateUser(w http.ResponseWriter, r *http.Request) {
   373  	method := "UpdateUser"
   374  	p := request.GetPersister(r)
   375  
   376  	params := mux.Vars(r)
   377  	userID := params["userID"]
   378  
   379  	if len(userID) == 0 {
   380  		writeBadRequestError(w, method, "user id must be numeric")
   381  		return
   382  	}
   383  
   384  	defer utility.Close(r.Body)
   385  	body, err := ioutil.ReadAll(r.Body)
   386  
   387  	if err != nil {
   388  		writePayloadError(w, method, err)
   389  		return
   390  	}
   391  
   392  	user := entity.User{}
   393  	err = json.Unmarshal(body, &user)
   394  
   395  	if err != nil {
   396  		writeJSONMarshalError(w, method, "user", err)
   397  		return
   398  	}
   399  
   400  	// can only update your own account unless you are an admin
   401  	if p.Context.UserID != userID && !p.Context.Administrator {
   402  		writeForbiddenError(w)
   403  		return
   404  	}
   405  
   406  	// can only update your own account unless you are an admin
   407  	if len(user.Email) == 0 {
   408  		writeBadRequestError(w, method, "missing email")
   409  		return
   410  	}
   411  
   412  	tx, err := request.Db.Beginx()
   413  
   414  	if err != nil {
   415  		writeTransactionError(w, method, err)
   416  		return
   417  	}
   418  
   419  	p.Context.Transaction = tx
   420  
   421  	user.RefID = userID
   422  	user.Initials = utility.MakeInitials(user.Firstname, user.Lastname)
   423  
   424  	err = p.UpdateUser(user)
   425  
   426  	if err != nil {
   427  		log.IfErr(tx.Rollback())
   428  		writeGeneralSQLError(w, method, err)
   429  		return
   430  	}
   431  
   432  	// Now we update user roles for this organization.
   433  	// That means we have to first find their account record
   434  	// for this organization.
   435  	account, err := p.GetUserAccount(userID)
   436  
   437  	if err != nil {
   438  		log.IfErr(tx.Rollback())
   439  		writeGeneralSQLError(w, method, err)
   440  		return
   441  	}
   442  
   443  	account.Editor = user.Editor
   444  	account.Admin = user.Admin
   445  
   446  	err = p.UpdateAccount(account)
   447  	if err != nil {
   448  		log.IfErr(tx.Rollback())
   449  		writeGeneralSQLError(w, method, err)
   450  		return
   451  	}
   452  
   453  	log.IfErr(tx.Commit())
   454  
   455  	json, err := json.Marshal(user)
   456  
   457  	if err != nil {
   458  		writeJSONMarshalError(w, method, "user", err)
   459  		return
   460  	}
   461  
   462  	writeSuccessBytes(w, json)
   463  }
   464  
   465  // ChangeUserPassword accepts password change from within the app.
   466  func ChangeUserPassword(w http.ResponseWriter, r *http.Request) {
   467  	method := "ChangeUserPassword"
   468  	p := request.GetPersister(r)
   469  
   470  	params := mux.Vars(r)
   471  	userID := params["userID"]
   472  
   473  	if len(userID) == 0 {
   474  		writeMissingDataError(w, method, "user id")
   475  		return
   476  	}
   477  
   478  	defer utility.Close(r.Body)
   479  	body, err := ioutil.ReadAll(r.Body)
   480  
   481  	if err != nil {
   482  		writePayloadError(w, method, err)
   483  		return
   484  	}
   485  
   486  	newPassword := string(body)
   487  
   488  	// can only update your own account unless you are an admin
   489  	if userID != p.Context.UserID && !p.Context.Administrator {
   490  		writeForbiddenError(w)
   491  		return
   492  	}
   493  
   494  	tx, err := request.Db.Beginx()
   495  
   496  	if err != nil {
   497  		writeTransactionError(w, method, err)
   498  		return
   499  	}
   500  
   501  	p.Context.Transaction = tx
   502  
   503  	user, err := p.GetUser(userID)
   504  
   505  	if err != nil {
   506  		writeGeneralSQLError(w, method, err)
   507  		return
   508  	}
   509  
   510  	user.Salt = util.GenerateSalt()
   511  
   512  	err = p.UpdateUserPassword(userID, user.Salt, util.GeneratePassword(newPassword, user.Salt))
   513  
   514  	if err != nil {
   515  		writeGeneralSQLError(w, method, err)
   516  		return
   517  	}
   518  
   519  	log.IfErr(tx.Commit())
   520  
   521  	writeSuccessEmptyJSON(w)
   522  }
   523  
   524  // GetUserFolderPermissions returns folder permission for authenticated user.
   525  func GetUserFolderPermissions(w http.ResponseWriter, r *http.Request) {
   526  	method := "ChangeUserPassword"
   527  	p := request.GetPersister(r)
   528  
   529  	params := mux.Vars(r)
   530  	userID := params["userID"]
   531  
   532  	if userID != p.Context.UserID {
   533  		writeUnauthorizedError(w)
   534  		return
   535  	}
   536  
   537  	roles, err := p.GetUserLabelRoles()
   538  
   539  	if err == sql.ErrNoRows {
   540  		err = nil
   541  		roles = []entity.LabelRole{}
   542  	}
   543  
   544  	if err != nil {
   545  		writeServerError(w, method, err)
   546  		return
   547  	}
   548  
   549  	json, err := json.Marshal(roles)
   550  
   551  	if err != nil {
   552  		writeJSONMarshalError(w, method, "roles", err)
   553  		return
   554  	}
   555  
   556  	writeSuccessBytes(w, json)
   557  }
   558  
   559  // ForgotUserPassword initiates the change password procedure.
   560  // Generates a reset token and sends email to the user.
   561  // User has to click link in email and then provide a new password.
   562  func ForgotUserPassword(w http.ResponseWriter, r *http.Request) {
   563  	method := "ForgotUserPassword"
   564  	p := request.GetPersister(r)
   565  
   566  	defer utility.Close(r.Body)
   567  	body, err := ioutil.ReadAll(r.Body)
   568  
   569  	if err != nil {
   570  		writeBadRequestError(w, method, "cannot ready payload")
   571  		return
   572  	}
   573  
   574  	user := new(entity.User)
   575  	err = json.Unmarshal(body, &user)
   576  
   577  	if err != nil {
   578  		writePayloadError(w, method, err)
   579  		return
   580  	}
   581  
   582  	tx, err := request.Db.Beginx()
   583  
   584  	if err != nil {
   585  		writeTransactionError(w, method, err)
   586  		return
   587  	}
   588  
   589  	p.Context.Transaction = tx
   590  
   591  	token := util.GenerateSalt()
   592  
   593  	err = p.ForgotUserPassword(user.Email, token)
   594  
   595  	if err != nil && err != sql.ErrNoRows {
   596  		log.IfErr(tx.Rollback())
   597  		writeGeneralSQLError(w, method, err)
   598  		return
   599  	}
   600  
   601  	if err == sql.ErrNoRows {
   602  		writeServerError(w, method, fmt.Errorf("User %s not found for password reset process", user.Email))
   603  		return
   604  	}
   605  
   606  	log.IfErr(tx.Commit())
   607  
   608  	appURL := getAppURL(p.Context, fmt.Sprintf("auth/reset/%s", token))
   609  
   610  	fmt.Println(appURL)
   611  
   612  	go mail.PasswordReset(user.Email, appURL)
   613  
   614  	writeSuccessEmptyJSON(w)
   615  }
   616  
   617  // ResetUserPassword stores the newly chosen password for the user.
   618  func ResetUserPassword(w http.ResponseWriter, r *http.Request) {
   619  	api.SetJSONResponse(w)
   620  	p := request.GetPersister(r)
   621  
   622  	params := mux.Vars(r)
   623  	token := params["token"]
   624  
   625  	if len(token) == 0 {
   626  		log.ErrorString("ResetUserPassword - missing password reset token")
   627  		api.WriteErrorBadRequest(w, "missing password reset token")
   628  		return
   629  	}
   630  
   631  	defer utility.Close(r.Body)
   632  	body, err := ioutil.ReadAll(r.Body)
   633  
   634  	if err != nil {
   635  		api.WriteErrorBadRequest(w, "Bad payload")
   636  		log.Error("ResetUserPassword - failed to read body", err)
   637  		return
   638  	}
   639  
   640  	newPassword := string(body)
   641  
   642  	tx, err := request.Db.Beginx()
   643  
   644  	if err != nil {
   645  		api.WriteError(w, err)
   646  		log.Error("ResetUserPassword - failed to get DB transaction", err)
   647  		return
   648  	}
   649  
   650  	p.Context.Transaction = tx
   651  
   652  	user, err := p.GetUserByToken(token)
   653  
   654  	if err != nil {
   655  		api.WriteError(w, err)
   656  		log.Error("ResetUserPassword - unable to retrieve user", err)
   657  		return
   658  	}
   659  
   660  	user.Salt = util.GenerateSalt()
   661  
   662  	err = p.UpdateUserPassword(user.RefID, user.Salt, util.GeneratePassword(newPassword, user.Salt))
   663  
   664  	if err != nil {
   665  		log.IfErr(tx.Rollback())
   666  		api.WriteError(w, err)
   667  		log.Error("ResetUserPassword - failed to change password", err)
   668  		return
   669  	}
   670  
   671  	log.IfErr(tx.Commit())
   672  
   673  	_, err = w.Write([]byte("{}"))
   674  	log.IfErr(err)
   675  }
   676  
   677  // Get user object contain associated accounts but credentials are wiped.
   678  func getSecuredUser(p request.Persister, orgID, user string) (u entity.User, err error) {
   679  	u, err = p.GetUser(user)
   680  	attachUserAccounts(p, orgID, &u)
   681  	return
   682  }
   683  
   684  func attachUserAccounts(p request.Persister, orgID string, user *entity.User) {
   685  	user.ProtectSecrets()
   686  	a, err := p.GetUserAccounts(user.RefID)
   687  
   688  	if err != nil {
   689  		log.Error("Unable to fetch user accounts", err)
   690  		return
   691  	}
   692  
   693  	user.Accounts = a
   694  	user.Editor = false
   695  	user.Admin = false
   696  
   697  	for _, account := range user.Accounts {
   698  		if account.OrgID == orgID {
   699  			user.Admin = account.Admin
   700  			user.Editor = account.Editor
   701  			break
   702  		}
   703  	}
   704  }
   705  
   706  func writeUsers(w http.ResponseWriter, u []entity.User) {
   707  	if u == nil {
   708  		u = []entity.User{}
   709  	}
   710  
   711  	j, err := json.Marshal(u)
   712  
   713  	if err != nil {
   714  		log.Error("unable to writeUsers", err)
   715  		writeServerError(w, "unabe to writeUsers", err)
   716  		return
   717  	}
   718  
   719  	writeSuccessBytes(w, j)
   720  }