github.com/readium/readium-lcp-server@v0.0.0-20240101192032-6e95190e99f1/frontend/api/user.go (about)

     1  // Copyright (c) 2016 Readium Foundation
     2  //
     3  // Redistribution and use in source and binary forms, with or without modification,
     4  // are permitted provided that the following conditions are met:
     5  //
     6  // 1. Redistributions of source code must retain the above copyright notice, this
     7  //    list of conditions and the following disclaimer.
     8  // 2. Redistributions in binary form must reproduce the above copyright notice,
     9  //    this list of conditions and the following disclaimer in the documentation and/or
    10  //    other materials provided with the distribution.
    11  // 3. Neither the name of the organization nor the names of its contributors may be
    12  //    used to endorse or promote products derived from this software without specific
    13  //    prior written permission
    14  //
    15  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    16  // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    17  // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    18  // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
    19  // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    20  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    21  // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    22  // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    23  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    24  // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    25  
    26  package staticapi
    27  
    28  import (
    29  	"encoding/json"
    30  	"net/http"
    31  	"strconv"
    32  
    33  	"github.com/gorilla/mux"
    34  	"github.com/readium/readium-lcp-server/api"
    35  	"github.com/readium/readium-lcp-server/frontend/webpurchase"
    36  	"github.com/readium/readium-lcp-server/frontend/webuser"
    37  	apilsd "github.com/readium/readium-lcp-server/lsdserver/api"
    38  	"github.com/readium/readium-lcp-server/problem"
    39  )
    40  
    41  //GetUsers returns a list of users
    42  func GetUsers(w http.ResponseWriter, r *http.Request, s IServer) {
    43  	var page int64
    44  	var perPage int64
    45  	var err error
    46  	if r.FormValue("page") != "" {
    47  		page, err = strconv.ParseInt((r).FormValue("page"), 10, 32)
    48  		if err != nil {
    49  			problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusBadRequest)
    50  			return
    51  		}
    52  	} else {
    53  		page = 1
    54  	}
    55  	if r.FormValue("per_page") != "" {
    56  		perPage, err = strconv.ParseInt((r).FormValue("per_page"), 10, 32)
    57  		if err != nil {
    58  			problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusBadRequest)
    59  			return
    60  		}
    61  	} else {
    62  		perPage = 30
    63  	}
    64  	if page > 0 {
    65  		page-- //pagenum starting at 0 in code, but user interface starting at 1
    66  	}
    67  	if page < 0 {
    68  		problem.Error(w, r, problem.Problem{Detail: "page must be positive integer"}, http.StatusBadRequest)
    69  		return
    70  	}
    71  	users := make([]webuser.User, 0)
    72  
    73  	fn := s.UserAPI().ListUsers(int(perPage), int(page))
    74  	for it, err := fn(); err == nil; it, err = fn() {
    75  		users = append(users, it)
    76  	}
    77  	if len(users) > 0 {
    78  		nextPage := strconv.Itoa(int(page) + 1)
    79  		w.Header().Set("Link", "</users/?page="+nextPage+">; rel=\"next\"; title=\"next\"")
    80  	}
    81  	if page > 1 {
    82  		previousPage := strconv.Itoa(int(page) - 1)
    83  		w.Header().Set("Link", "</users/?page="+previousPage+">; rel=\"previous\"; title=\"previous\"")
    84  	}
    85  	w.Header().Set("Content-Type", api.ContentType_JSON)
    86  	enc := json.NewEncoder(w)
    87  	err = enc.Encode(users)
    88  	if err != nil {
    89  		problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusBadRequest)
    90  		return
    91  	}
    92  }
    93  
    94  //GetUser searches a client by his email
    95  func GetUser(w http.ResponseWriter, r *http.Request, s IServer) {
    96  	vars := mux.Vars(r)
    97  	id, err := strconv.Atoi(vars["id"])
    98  	if err != nil {
    99  		// id is not a number
   100  		problem.Error(w, r, problem.Problem{Detail: "User ID must be an integer"}, http.StatusBadRequest)
   101  	}
   102  	if user, err := s.UserAPI().Get(int64(id)); err == nil {
   103  		w.Header().Set("Content-Type", api.ContentType_JSON)
   104  		enc := json.NewEncoder(w)
   105  		if err = enc.Encode(user); err == nil {
   106  			return
   107  		}
   108  		problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
   109  	} else {
   110  		switch err {
   111  		case webuser.ErrNotFound:
   112  			{
   113  				problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusNotFound)
   114  			}
   115  		default:
   116  			{
   117  				problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
   118  			}
   119  		}
   120  	}
   121  }
   122  
   123  // GetLicenseOwner retrieves a user by a license uuid he owns
   124  func GetLicenseOwner(w http.ResponseWriter, r *http.Request, s IServer) {
   125  
   126  	vars := mux.Vars(r)
   127  	licenseID := vars["license_id"]
   128  
   129  	// get the purchase related to the license
   130  	purchase, err := s.PurchaseAPI().GetByLicenseID(licenseID)
   131  	if err != nil {
   132  		switch err {
   133  		case webpurchase.ErrNotFound:
   134  			{
   135  				problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusNotFound)
   136  			}
   137  		default:
   138  			{
   139  				problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
   140  			}
   141  		}
   142  	}
   143  
   144  	// get the corresponding user info
   145  	user, err := s.UserAPI().Get(purchase.User.ID)
   146  	if err != nil {
   147  		switch err {
   148  		case webuser.ErrNotFound:
   149  			{
   150  				problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusNotFound)
   151  			}
   152  		default:
   153  			{
   154  				problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
   155  			}
   156  		}
   157  	}
   158  
   159  	// map user info to a shared structure
   160  	userData := apilsd.UserData{}
   161  	userData.ID = user.UUID
   162  	userData.Name = user.Name
   163  	userData.Email = user.Email
   164  	userData.Hint = user.Hint
   165  	userData.PassphraseHash = user.Password
   166  
   167  	w.Header().Set("Content-Type", api.ContentType_JSON)
   168  	// json encode user info
   169  	enc := json.NewEncoder(w)
   170  	err = enc.Encode(userData)
   171  	if err != nil {
   172  		problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
   173  		return
   174  	}
   175  }
   176  
   177  //DecodeJSONUser transforms a json string to a User struct
   178  func DecodeJSONUser(r *http.Request) (webuser.User, error) {
   179  	var dec *json.Decoder
   180  	if ctype := r.Header["Content-Type"]; len(ctype) > 0 && ctype[0] == api.ContentType_JSON {
   181  		dec = json.NewDecoder(r.Body)
   182  	}
   183  	user := webuser.User{}
   184  	err := dec.Decode(&user)
   185  	return user, err
   186  }
   187  
   188  //CreateUser creates a user in the database
   189  func CreateUser(w http.ResponseWriter, r *http.Request, s IServer) {
   190  	var user webuser.User
   191  	var err error
   192  	if user, err = DecodeJSONUser(r); err != nil {
   193  		problem.Error(w, r, problem.Problem{Detail: "incorrect JSON User " + err.Error()}, http.StatusBadRequest)
   194  		return
   195  	}
   196  	//user ok
   197  	if err := s.UserAPI().Add(user); err != nil {
   198  		problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusBadRequest)
   199  		return
   200  	}
   201  	// user added to db
   202  	w.WriteHeader(http.StatusCreated)
   203  }
   204  
   205  //UpdateUser updates an identified user (id) in the database
   206  func UpdateUser(w http.ResponseWriter, r *http.Request, s IServer) {
   207  	vars := mux.Vars(r)
   208  	var id int
   209  	var err error
   210  	var user webuser.User
   211  	if id, err = strconv.Atoi(vars["id"]); err != nil {
   212  		// id is not a number
   213  		problem.Error(w, r, problem.Problem{Detail: "User ID must be an integer"}, http.StatusBadRequest)
   214  		return
   215  	}
   216  	//ID is a number, check user (json)
   217  	if user, err = DecodeJSONUser(r); err != nil {
   218  		problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusBadRequest)
   219  		return
   220  	}
   221  	// user ok, id is a number, search user to update
   222  	if _, err := s.UserAPI().Get(int64(id)); err != nil {
   223  		switch err {
   224  		case webuser.ErrNotFound:
   225  			problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusNotFound)
   226  		default:
   227  			problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
   228  		}
   229  	} else {
   230  		// client is found!
   231  		if err := s.UserAPI().Update(webuser.User{ID: int64(id), Name: user.Name, Email: user.Email, Password: user.Password, Hint: user.Hint}); err != nil {
   232  			//update failed!
   233  			problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
   234  			return
   235  		}
   236  		//database update ok
   237  		w.WriteHeader(http.StatusOK)
   238  		//return
   239  	}
   240  
   241  }
   242  
   243  //DeleteUser creates a user in the database
   244  func DeleteUser(w http.ResponseWriter, r *http.Request, s IServer) {
   245  	vars := mux.Vars(r)
   246  	uid, err := strconv.ParseInt(vars["id"], 10, 64)
   247  	if err != nil {
   248  		problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusBadRequest)
   249  		return
   250  	}
   251  	if err := s.UserAPI().DeleteUser(uid); err != nil {
   252  		problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusBadRequest)
   253  		return
   254  	}
   255  	// user added to db
   256  	w.WriteHeader(http.StatusOK)
   257  }