Skip to content

Commit

Permalink
Merge pull request #263 from readium/develop
Browse files Browse the repository at this point in the history
Fetch a fresh license via the Status Doc Server
  • Loading branch information
llemeurfr authored Dec 27, 2021
2 parents 626f496 + 7a1d5bb commit a7ae277
Show file tree
Hide file tree
Showing 12 changed files with 393 additions and 24 deletions.
6 changes: 4 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type Configuration struct {
FrontendServer FrontendServerInfo `yaml:"frontend"`
LsdNotifyAuth Auth `yaml:"lsd_notify_auth"`
LcpUpdateAuth Auth `yaml:"lcp_update_auth"`
CMSAccessAuth Auth `yaml:"cms_access_auth"`
LicenseStatus LicenseStatus `yaml:"license_status"`
Localization Localization `yaml:"localization"`
ComplianceMode bool `yaml:"compliance_mode"`
Expand All @@ -66,6 +67,7 @@ type ServerInfo struct {
type LsdServerInfo struct {
ServerInfo `yaml:",inline"`
LicenseLinkUrl string `yaml:"license_link_url,omitempty"`
UserDataUrl string `yaml:"user_data_url,omitempty"`
LogDirectory string `yaml:"log_directory"`
}

Expand Down Expand Up @@ -113,8 +115,8 @@ type LicenseStatus struct {
Renew bool `yaml:"renew"`
Register bool `yaml:"register"`
Return bool `yaml:"return"`
RentingDays int `yaml:"renting_days" "default 0"`
RenewDays int `yaml:"renew_days" "default 0"`
RentingDays int `yaml:"renting_days"`
RenewDays int `yaml:"renew_days"`
RenewPageUrl string `yaml:"renew_page_url,omitempty"`
}

Expand Down
62 changes: 58 additions & 4 deletions frontend/api/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ import (

"github.com/gorilla/mux"
"github.com/readium/readium-lcp-server/api"
"github.com/readium/readium-lcp-server/frontend/webpurchase"
"github.com/readium/readium-lcp-server/frontend/webuser"
apilsd "github.com/readium/readium-lcp-server/lsdserver/api"
"github.com/readium/readium-lcp-server/problem"
)

Expand Down Expand Up @@ -81,7 +83,6 @@ func GetUsers(w http.ResponseWriter, r *http.Request, s IServer) {
w.Header().Set("Link", "</users/?page="+previousPage+">; rel=\"previous\"; title=\"previous\"")
}
w.Header().Set("Content-Type", api.ContentType_JSON)

enc := json.NewEncoder(w)
err = enc.Encode(users)
if err != nil {
Expand All @@ -99,11 +100,9 @@ func GetUser(w http.ResponseWriter, r *http.Request, s IServer) {
problem.Error(w, r, problem.Problem{Detail: "User ID must be an integer"}, http.StatusBadRequest)
}
if user, err := s.UserAPI().Get(int64(id)); err == nil {
w.Header().Set("Content-Type", api.ContentType_JSON)
enc := json.NewEncoder(w)
if err = enc.Encode(user); err == nil {
// send json of correctly encoded user info
w.Header().Set("Content-Type", api.ContentType_JSON)
w.WriteHeader(http.StatusOK)
return
}
problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
Expand All @@ -122,6 +121,61 @@ func GetUser(w http.ResponseWriter, r *http.Request, s IServer) {
return
}

// GetLicenseOwner retrieves a user by a license uuid he owns
func GetLicenseOwner(w http.ResponseWriter, r *http.Request, s IServer) {

vars := mux.Vars(r)
licenseID := vars["license_id"]

// get the purchase related to the license
purchase, err := s.PurchaseAPI().GetByLicenseID(licenseID)
if err != nil {
switch err {
case webpurchase.ErrNotFound:
{
problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusNotFound)
}
default:
{
problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
}
}
}

// get the corresponding user info
user, err := s.UserAPI().Get(purchase.User.ID)
if err != nil {
switch err {
case webuser.ErrNotFound:
{
problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusNotFound)
}
default:
{
problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
}
}
}

// map user info to a shared structure
userData := apilsd.UserData{}
userData.ID = user.UUID
userData.Name = user.Name
userData.Email = user.Email
userData.Hint = user.Hint
userData.PassphraseHash = user.Password

w.Header().Set("Content-Type", api.ContentType_JSON)
// json encode user info
enc := json.NewEncoder(w)
err = enc.Encode(userData)
if err != nil {
problem.Error(w, r, problem.Problem{Detail: err.Error()}, http.StatusInternalServerError)
return
}
return
}

//DecodeJSONUser transforms a json string to a User struct
func DecodeJSONUser(r *http.Request) (webuser.User, error) {
var dec *json.Decoder
Expand Down
19 changes: 17 additions & 2 deletions frontend/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ import (
"strings"
"syscall"

auth "github.com/abbot/go-http-auth"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"

"github.com/readium/readium-lcp-server/config"
"github.com/readium/readium-lcp-server/frontend/server"
frontend "github.com/readium/readium-lcp-server/frontend/server"
"github.com/readium/readium-lcp-server/frontend/webdashboard"
"github.com/readium/readium-lcp-server/frontend/weblicense"
"github.com/readium/readium-lcp-server/frontend/webpublication"
Expand Down Expand Up @@ -153,7 +154,21 @@ func main() {

fileConfigJs.WriteString(configJs)
HandleSignals()
s := frontend.New(config.Config.FrontendServer.Host+":"+strconv.Itoa(config.Config.FrontendServer.Port), static, repoManager, publicationDB, userDB, dashboardDB, licenseDB, purchaseDB)

// basic authentication, optional in the frontend server.
// Authentication is used for getting user info from a license id.
var authenticator *auth.BasicAuth
authFile := config.Config.LsdServer.AuthFile
if authFile != "" {
_, err = os.Stat(authFile)
if err != nil {
panic(err)
}
htpasswd := auth.HtpasswdFileProvider(authFile)
authenticator = auth.NewBasicAuthenticator("Basic Realm", htpasswd)
}

s := frontend.New(config.Config.FrontendServer.Host+":"+strconv.Itoa(config.Config.FrontendServer.Port), static, repoManager, publicationDB, userDB, dashboardDB, licenseDB, purchaseDB, authenticator)
log.Println("Frontend webserver for LCP running on " + config.Config.FrontendServer.Host + ":" + strconv.Itoa(config.Config.FrontendServer.Port))
log.Println("using database " + dbURI)

Expand Down
17 changes: 12 additions & 5 deletions frontend/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"net/http"
"time"

auth "github.com/abbot/go-http-auth"
"github.com/claudiu/gocron"
"github.com/gorilla/mux"
"github.com/readium/readium-lcp-server/api"
Expand Down Expand Up @@ -63,7 +64,7 @@ type Server struct {
// HandlerFunc defines a function handled by the server
type HandlerFunc func(w http.ResponseWriter, r *http.Request, s staticapi.IServer)

//type HandlerPrivateFunc func(w http.ResponseWriter, r *auth.AuthenticatedRequest, s staticapi.IServer)
type HandlerPrivateFunc func(w http.ResponseWriter, r *auth.AuthenticatedRequest, s staticapi.IServer)

// New creates a new webserver (basic user interface)
func New(
Expand All @@ -74,14 +75,15 @@ func New(
userAPI webuser.WebUser,
dashboardAPI webdashboard.WebDashboard,
licenseAPI weblicense.WebLicense,
purchaseAPI webpurchase.WebPurchase) *Server {
purchaseAPI webpurchase.WebPurchase,
basicAuth *auth.BasicAuth) *Server {

sr := api.CreateServerRouter(tplPath)
s := &Server{
Server: http.Server{
Handler: sr.N,
Addr: bindAddr,
WriteTimeout: 20 * time.Second,
WriteTimeout: 150 * time.Second,
ReadTimeout: 150 * time.Second,
MaxHeaderBytes: 1 << 20,
},
Expand Down Expand Up @@ -168,10 +170,16 @@ func New(
s.handleFunc(sr.R, licenseRoutesPathPrefix, staticapi.GetFilteredLicenses).Methods("GET")
// get a license by id
s.handleFunc(licenseRoutes, "/{license_id}", staticapi.GetLicense).Methods("GET")
// get the user who owns a given license; this route is only set if authentication is in use
if basicAuth != nil {
s.handlePrivateFunc(licenseRoutes, "/{license_id}/user", staticapi.GetLicenseOwner, basicAuth).Methods("GET")
}

return s
}

// fetchLicenseStatusesTask fetchs from the Status Doc Server, and saves, locally, all license status documents.
// This is optimizing the visualization of status information in the UI.
func fetchLicenseStatusesTask(s *Server) {
fmt.Println("AUTOMATIC : Fetch and save all license status documents")
url := config.Config.LsdServer.PublicBaseUrl + "/licenses"
Expand Down Expand Up @@ -237,18 +245,17 @@ func (server *Server) LicenseAPI() weblicense.WebLicense {
return server.license
}

// mux handle functions
func (server *Server) handleFunc(router *mux.Router, route string, fn HandlerFunc) *mux.Route {
return router.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) {
fn(w, r, server)
})
}

/*no private functions used
func (server *Server) handlePrivateFunc(router *mux.Router, route string, fn HandlerFunc, authenticator *auth.BasicAuth) *mux.Route {
return router.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) {
if api.CheckAuth(authenticator, w, r) {
fn(w, r, server)
}
})
}
*/
11 changes: 5 additions & 6 deletions frontend/webpurchase/webpurchase.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,17 @@ const (
//Purchase struct defines a user in json and database
//PurchaseType: BUY or LOAN
type Purchase struct {
ID int64 `json:"id, omitempty"`
ID int64 `json:"id,omitempty"`
UUID string `json:"uuid"`
Publication webpublication.Publication `json:"publication"`
User webuser.User `json:"user"`
LicenseUUID *string `json:"licenseUuid,omitempty"`
Type string `json:"type"`
TransactionDate time.Time `json:"transactionDate, omitempty"`
StartDate *time.Time `json:"startDate, omitempty"`
EndDate *time.Time `json:"endDate, omitempty"`
TransactionDate time.Time `json:"transactionDate,omitempty"`
StartDate *time.Time `json:"startDate,omitempty"`
EndDate *time.Time `json:"endDate,omitempty"`
Status string `json:"status"`
MaxEndDate *time.Time `json:"maxEndDate, omitempty"`
MaxEndDate *time.Time `json:"maxEndDate,omitempty"`
}

type PurchaseManager struct {
Expand Down Expand Up @@ -338,7 +338,6 @@ func (pManager PurchaseManager) GetPartialLicense(purchase Purchase) (license.Li
if err != nil {
return license.License{}, err
}
// FIXME: why this Close()?
defer resp.Body.Close()

// the call must return 206 (partial content) because there is no input partial license
Expand Down
2 changes: 1 addition & 1 deletion lcpserver/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func New(bindAddr string, readonly bool, idx *index.Index, st *storage.Store, ls
Handler: sr.N,
Addr: bindAddr,
WriteTimeout: 240 * time.Second,
ReadTimeout: 5 * time.Second,
ReadTimeout: 15 * time.Second,
MaxHeaderBytes: 1 << 20,
},
readonly: readonly,
Expand Down
Loading

0 comments on commit a7ae277

Please sign in to comment.