summaryrefslogtreecommitdiff
path: root/internal/discovery
diff options
context:
space:
mode:
authorjwijenbergh <jeroenwijenbergh@protonmail.com>2022-06-17 14:00:40 +0200
committerjwijenbergh <jeroenwijenbergh@protonmail.com>2022-09-20 20:31:23 +0200
commit7af07c596166bf93b79a9d0816b1950dde360fb9 (patch)
tree08b5374c34d6c33b3c596ed981bfb069cca37ade /internal/discovery
parent6dc7b64f634f6dcbeedea24c741382366a3c7b8c (diff)
Server: Implement function for checking renewal button visibility
Diffstat (limited to 'internal/discovery')
-rw-r--r--internal/discovery/discovery.go177
1 files changed, 177 insertions, 0 deletions
diff --git a/internal/discovery/discovery.go b/internal/discovery/discovery.go
new file mode 100644
index 0000000..d72b4a6
--- /dev/null
+++ b/internal/discovery/discovery.go
@@ -0,0 +1,177 @@
+package discovery
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/jwijenbergh/eduvpn-common/internal/fsm"
+ "github.com/jwijenbergh/eduvpn-common/internal/http"
+ "github.com/jwijenbergh/eduvpn-common/internal/log"
+ "github.com/jwijenbergh/eduvpn-common/internal/util"
+ "github.com/jwijenbergh/eduvpn-common/internal/verify"
+)
+
+type DiscoFileError struct {
+ URL string
+ Err error
+}
+
+func (e *DiscoFileError) Error() string {
+ return fmt.Sprintf("failed obtaining disco file %s with error %v", e.URL, e.Err)
+}
+
+type DiscoSigFileError struct {
+ URL string
+ Err error
+}
+
+func (e *DiscoSigFileError) Error() string {
+ return fmt.Sprintf("failed obtaining disco signature file %s with error %v", e.URL, e.Err)
+}
+
+type DiscoVerifyError struct {
+ File string
+ Sigfile string
+ Err error
+}
+
+func (e *DiscoVerifyError) Error() string {
+ return fmt.Sprintf("failed verifying file %s with signature %s due to error %v", e.File, e.Sigfile, e.Err)
+}
+
+type DiscoJSONError struct {
+ Body string
+ Err error
+}
+
+func (e *DiscoJSONError) Error() string {
+ return fmt.Sprintf("failed parsing JSON for contents %s with error %v", e.Body, e.Err)
+}
+
+type OrganizationList struct {
+ JSON json.RawMessage `json:"organization_list"`
+ Version uint64 `json:"v"`
+ Timestamp int64 `json:"-"`
+}
+
+type ServersList struct {
+ JSON json.RawMessage `json:"server_list"`
+ Version uint64 `json:"v"`
+ Timestamp int64 `json:"-"`
+}
+
+type Discovery struct {
+ Organizations OrganizationList
+ Servers ServersList
+ FSM *fsm.FSM
+ Logger *log.FileLogger
+}
+
+// Helper function that gets a disco json
+func getDiscoFile(jsonFile string, previousVersion uint64, structure interface{}) error {
+ // Get json data
+ discoURL := "https://disco.eduvpn.org/v2/"
+ fileURL := discoURL + jsonFile
+ _, fileBody, fileErr := http.HTTPGet(fileURL)
+
+ if fileErr != nil {
+ return &DiscoFileError{fileURL, fileErr}
+ }
+
+ // Get signature
+ sigFile := jsonFile + ".minisig"
+ sigURL := discoURL + sigFile
+ _, sigBody, sigFileErr := http.HTTPGet(sigURL)
+
+ if sigFileErr != nil {
+ return &DiscoSigFileError{URL: sigURL, Err: sigFileErr}
+ }
+
+ // Verify signature
+ // Set this to true when we want to force prehash
+ forcePrehash := false
+ verifySuccess, verifyErr := verify.Verify(string(sigBody), fileBody, jsonFile, previousVersion, forcePrehash)
+
+ if !verifySuccess || verifyErr != nil {
+ return &DiscoVerifyError{File: jsonFile, Sigfile: sigFile, Err: verifyErr}
+ }
+
+ // Parse JSON to extract version and list
+ jsonErr := json.Unmarshal(fileBody, structure)
+
+ if jsonErr != nil {
+ return &DiscoJSONError{Body: string(fileBody), Err: jsonErr}
+ }
+
+ return nil
+}
+
+type GetListError struct {
+ File string
+ Err error
+}
+
+func (e *GetListError) Error() string {
+ return fmt.Sprintf("failed getting disco list file %s with error %v", e.File, e.Err)
+}
+
+func (discovery *Discovery) Init(fsm *fsm.FSM, logger *log.FileLogger) {
+ discovery.FSM = fsm
+ discovery.Logger = logger
+}
+
+// FIXME: Implement based on
+// https://github.com/eduvpn/documentation/blob/v3/SERVER_DISCOVERY.md
+// - [IMPLEMENTED] on "first launch" when offering the search for "Institute Access" and "Organizations";
+// - [TODO] when the user tries to add new server AND the user did NOT yet choose an organization before;
+// - [TODO] when the authorization for the server associated with an already chosen organization is triggered, e.g. after expiry or revocation.
+func (discovery *Discovery) DetermineOrganizationsUpdate() bool {
+ return string(discovery.Organizations.JSON) == ""
+}
+
+// https://github.com/eduvpn/documentation/blob/v3/SERVER_DISCOVERY.md
+// - [Implemented] The application MUST always fetch the server_list.json at application start.
+// - The application MAY refresh the server_list.json periodically, e.g. once every hour.
+func (discovery *Discovery) DetermineServersUpdate() bool {
+ // No servers, we should update
+ if string(discovery.Servers.JSON) == "" {
+ return true
+ }
+ // 1 hour from the last update
+ should_update_time := discovery.Servers.Timestamp + 3600
+ now := util.GenerateTimeSeconds()
+ if now >= should_update_time {
+ return true
+ }
+ discovery.Logger.Log(log.LOG_INFO, "No update needed for servers, 1h is not passed yet")
+ return false
+}
+
+// Get the organization list
+func (discovery *Discovery) GetOrganizationsList() (string, error) {
+ if !discovery.DetermineOrganizationsUpdate() {
+ return string(discovery.Organizations.JSON), nil
+ }
+ file := "organization_list.json"
+ err := getDiscoFile(file, discovery.Organizations.Version, &discovery.Organizations)
+ if err != nil {
+ // Return previous with an error
+ return string(discovery.Organizations.JSON), &GetListError{File: file, Err: err}
+ }
+ return string(discovery.Organizations.JSON), nil
+}
+
+// Get the server list
+func (discovery *Discovery) GetServersList() (string, error) {
+ if !discovery.DetermineServersUpdate() {
+ return string(discovery.Servers.JSON), nil
+ }
+ file := "server_list.json"
+ err := getDiscoFile(file, discovery.Servers.Version, &discovery.Servers)
+ if err != nil {
+ // Return previous with an error
+ return string(discovery.Servers.JSON), &GetListError{File: file, Err: err}
+ }
+ // Update servers timestamp
+ discovery.Servers.Timestamp = util.GenerateTimeSeconds()
+ return string(discovery.Servers.JSON), nil
+}