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 }