summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/server/api/api.go217
-rw-r--r--internal/server/api/api_test.go150
-rw-r--r--internal/server/base/base.go90
-rw-r--r--internal/server/custom/custom.go39
-rw-r--r--internal/server/endpoints/endpoints.go56
-rw-r--r--internal/server/institute/institute.go133
-rw-r--r--internal/server/list.go180
-rw-r--r--internal/server/profile/profile.go88
-rw-r--r--internal/server/profile/profile_test.go100
-rw-r--r--internal/server/secure/secure.go185
10 files changed, 0 insertions, 1238 deletions
diff --git a/internal/server/api/api.go b/internal/server/api/api.go
deleted file mode 100644
index 9ad6f2d..0000000
--- a/internal/server/api/api.go
+++ /dev/null
@@ -1,217 +0,0 @@
-package api
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "net/url"
- "path"
- "time"
-
- httpw "github.com/eduvpn/eduvpn-common/internal/http"
- "github.com/eduvpn/eduvpn-common/internal/log"
- "github.com/eduvpn/eduvpn-common/internal/oauth"
- "github.com/eduvpn/eduvpn-common/internal/server/base"
- "github.com/eduvpn/eduvpn-common/internal/server/endpoints"
- "github.com/eduvpn/eduvpn-common/internal/server/profile"
- "github.com/go-errors/errors"
-)
-
-func Endpoints(ctx context.Context, b *base.Base) error {
- uStr, err := httpw.JoinURLPath(b.URL, "/.well-known/vpn-user-portal")
- if err != nil {
- return err
- }
- if b.HTTPClient == nil {
- b.HTTPClient = httpw.NewClient()
- }
- _, body, err := b.HTTPClient.Get(ctx, uStr)
- if err != nil {
- return errors.WrapPrefix(err, "failed getting server endpoints", 0)
- }
-
- ep := endpoints.Endpoints{}
- if err = json.Unmarshal(body, &ep); err != nil {
- return errors.WrapPrefix(err, "failed getting server endpoints", 0)
- }
- err = ep.Validate()
- if err != nil {
- return err
- }
-
- b.Endpoints = ep
- return nil
-}
-
-func authorized(
- ctx context.Context,
- b *base.Base,
- oauth *oauth.OAuth,
- method string,
- endpoint string,
- opts *httpw.OptionalParams,
-) (http.Header, []byte, error) {
- // Ensure optional is not nil as we will fill it with headers
- if opts == nil {
- opts = &httpw.OptionalParams{}
- }
- errorMessage := "failed API authorized"
-
- // Join the paths
- u, err := url.Parse(b.Endpoints.API.V3.API)
- if err != nil {
- return nil, nil, errors.WrapPrefix(err, errorMessage, 0)
- }
- u.Path = path.Join(u.Path, endpoint)
-
- // Make sure the tokens are valid, this will return an error if re-login is needed
- t, err := oauth.AccessToken(ctx)
- if err != nil {
- return nil, nil, errors.WrapPrefix(err, errorMessage, 0)
- }
-
- key := "Authorization"
- val := fmt.Sprintf("Bearer %s", t)
- if opts.Headers != nil {
- opts.Headers.Add(key, val)
- } else {
- opts.Headers = http.Header{key: {val}}
- }
-
- // Create a client if it doesn't exist
- if b.HTTPClient == nil {
- b.HTTPClient = httpw.NewClient()
- }
- return b.HTTPClient.Do(ctx, method, u.String(), opts)
-}
-
-func authorizedRetry(
- ctx context.Context,
- b *base.Base,
- auth *oauth.OAuth,
- method string,
- endpoint string,
- opts *httpw.OptionalParams,
-) (http.Header, []byte, error) {
- h, body, err := authorized(ctx, b, auth, method, endpoint, opts)
- if err == nil {
- return h, body, nil
- }
-
- statErr := &httpw.StatusError{}
- // Only retry authorized if we get an HTTP 401
- if errors.As(err, &statErr) && statErr.Status == 401 {
- log.Logger.Debugf("Got a 401 error after HTTP method: %s, endpoint: %s. Marking token as expired...", method, endpoint)
- // Mark the token as expired and retry, so we trigger the refresh flow
- auth.SetTokenExpired()
- h, body, err = authorized(ctx, b, auth, method, endpoint, opts)
- }
- return h, body, err
-}
-
-func Info(ctx context.Context, b *base.Base, auth *oauth.OAuth) error {
- _, body, err := authorizedRetry(ctx, b, auth, http.MethodGet, "/info", nil)
- if err != nil {
- return err
- }
- profiles := profile.Info{}
- if err = json.Unmarshal(body, &profiles); err != nil {
- return errors.WrapPrefix(err, "failed API /info", 0)
- }
-
- // Store the profiles and make sure that the current profile is not overwritten
- prev := b.Profiles.Current
- b.Profiles = profiles
- b.Profiles.Current = prev
- return nil
-}
-
-// see https://github.com/eduvpn/documentation/blob/v3/API.md#request-1
-func boolToYesNo(preferTCP bool) string {
- if preferTCP {
- return "yes"
- }
- return "no"
-}
-
-func ConnectWireguard(
- ctx context.Context,
- b *base.Base,
- auth *oauth.OAuth,
- profileID string,
- pubkey string,
- preferTCP bool,
- openVPNSupport bool,
-) (string, string, time.Time, error) {
- hdrs := http.Header{
- "content-type": {"application/x-www-form-urlencoded"},
- "accept": {"application/x-wireguard-profile"},
- }
-
- // This profile also supports OpenVPN
- // Indicate that we also accept OpenVPN profiles
- if openVPNSupport {
- hdrs.Add("accept", "application/x-openvpn-profile")
- }
-
- vals := url.Values{
- "profile_id": {profileID},
- "public_key": {pubkey},
- "prefer_tcp": {boolToYesNo(preferTCP)},
- }
- h, body, err := authorizedRetry(ctx, b, auth, http.MethodPost, "/connect",
- &httpw.OptionalParams{Headers: hdrs, Body: vals})
- if err != nil {
- return "", "", time.Time{}, err
- }
-
- exp := h.Get("expires")
-
- expTime, err := http.ParseTime(exp)
- if err != nil {
- return "", "", time.Time{}, errors.WrapPrefix(err, "failed obtaining a WireGuard configuration", 0)
- }
-
- contentH := h.Get("content-type")
- content := "openvpn"
- if contentH == "application/x-wireguard-profile" {
- content = "wireguard"
- }
-
- return string(body), content, expTime, nil
-}
-
-func ConnectOpenVPN(ctx context.Context, b *base.Base, auth *oauth.OAuth, profileID string, preferTCP bool) (string, time.Time, error) {
- hdrs := http.Header{
- "content-type": {"application/x-www-form-urlencoded"},
- "accept": {"application/x-openvpn-profile"},
- }
-
- vals := url.Values{
- "profile_id": {profileID},
- "prefer_tcp": {boolToYesNo(preferTCP)},
- }
-
- h, body, err := authorizedRetry(ctx, b, auth, http.MethodPost, "/connect",
- &httpw.OptionalParams{Headers: hdrs, Body: vals})
- if err != nil {
- return "", time.Time{}, err
- }
-
- expH := h.Get("expires")
- expT, err := http.ParseTime(expH)
- if err != nil {
- return "", time.Time{}, errors.WrapPrefix(err, "failed obtaining an OpenVPN configuration", 0)
- }
-
- return string(body), expT, nil
-}
-
-// Disconnect disconnects the VPN using the API.
-func Disconnect(ctx context.Context, b *base.Base, auth *oauth.OAuth) error {
- // The timeout is a bit lower here such that this does not take a too long time for disconnecting
- // Clients may wish to retry this
- _, _, err := authorized(ctx, b, auth, http.MethodPost, "/disconnect", &httpw.OptionalParams{Timeout: 5 * time.Second})
- return err
-}
diff --git a/internal/server/api/api_test.go b/internal/server/api/api_test.go
deleted file mode 100644
index 2fea4c6..0000000
--- a/internal/server/api/api_test.go
+++ /dev/null
@@ -1,150 +0,0 @@
-package api
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "testing"
-
- "github.com/eduvpn/eduvpn-common/internal/server/base"
- "github.com/eduvpn/eduvpn-common/internal/server/endpoints"
- "github.com/eduvpn/eduvpn-common/internal/test"
- "github.com/go-errors/errors"
-)
-
-func getErrorMsg(err error) string {
- if err == nil {
- return ""
- }
- return err.Error()
-}
-
-func compareEndpoints(ep1 endpoints.Endpoints, ep2 endpoints.Endpoints) bool {
- v3_1 := ep1.API.V3
- v3_2 := ep2.API.V3
- return v3_1.API == v3_2.API && v3_1.Authorization == v3_2.Authorization && v3_1.Token == v3_2.Token
-}
-
-func Test_APIGetEndpoints(t *testing.T) {
- handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintln(w, "Hello!")
- })
- hs := &test.HandlerSet{}
- hs.SetHandler(handler)
- s := test.NewServer(hs)
- defer s.Close()
-
- c, err := s.Client()
- if err != nil {
- t.Fatalf("failed to get client for test server endpoints: %v", err)
- }
-
- testCases := []struct {
- epl endpoints.List
- err error
- }{
- {
- epl: endpoints.List{
- API: "https://example.com/1",
- Authorization: "https://example.com/2",
- Token: "https://example.com/3",
- },
- err: nil,
- },
- {
- epl: endpoints.List{
- API: "https://example.com/1",
- Authorization: "http://example.com/2",
- Token: "http://example.com/3",
- },
- err: errors.New("API scheme: 'https', is not equal to authorization scheme: 'http'"),
- },
- {
- epl: endpoints.List{
- API: "https://example.com/1",
- Authorization: "https://example.com/2",
- Token: "ftp://example.com/3",
- },
- err: errors.New("API scheme: 'https', is not equal to token scheme: 'ftp'"),
- },
- {
- epl: endpoints.List{
- API: "https://malicious.com/1",
- Authorization: "https://example.com/2",
- Token: "https://example.com/3",
- },
- err: errors.New("API host: 'malicious.com', is not equal to authorization host: 'example.com'"),
- },
- {
- epl: endpoints.List{
- API: "https://example.com/1",
- Authorization: "https://example.com/2",
- Token: "https://malicious.com/3",
- },
- err: errors.New("API host: 'example.com', is not equal to token host: 'malicious.com'"),
- },
- {
- epl: endpoints.List{
- API: "https://example.com/1",
- Authorization: "https://malicious.com/2",
- Token: "https://example.com/3",
- },
- err: errors.New("API host: 'example.com', is not equal to authorization host: 'malicious.com'"),
- },
- {
- epl: endpoints.List{
- API: "https://example.com/1",
- Authorization: "https://example.com/2",
- Token: "ftp://example.com/3",
- },
- err: errors.New("API scheme: 'https', is not equal to token scheme: 'ftp'"),
- },
- {
- epl: endpoints.List{
- API: "https://example.com/1",
- Authorization: "ftp://example.com/2",
- Token: "https://example.com/3",
- },
- err: errors.New("API scheme: 'https', is not equal to authorization scheme: 'ftp'"),
- },
- }
-
- for _, tc := range testCases {
- ep := &endpoints.Endpoints{
- API: endpoints.Versions{
- V3: tc.epl,
- },
- }
- // Update the handler
- hs.SetHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
-
- jsonStr, err := json.Marshal(ep)
- if err != nil {
- t.Fatalf("failed to marshal JSON for test case: %v, err: %v", tc, err)
- }
-
- fmt.Fprintln(w, string(jsonStr))
- }))
- b := &base.Base{
- URL: s.URL,
- HTTPClient: c,
- }
- err = Endpoints(context.Background(), b)
- if getErrorMsg(err) != getErrorMsg(tc.err) {
- t.Fatalf("Errors not equal, want err: %v, got: %v", tc.err, err)
- }
- // The error was not nil, continue because endpoints should not be compared
- if tc.err != nil {
- continue
- }
- if ep == nil {
- t.Fatalf("No test case endpoints")
- }
- // if no error then the endpoints should be equal
- if !compareEndpoints(*ep, b.Endpoints) {
- t.Fatalf("Endpoints are not equal, got: %v, want: %v", b.Endpoints, ep)
- }
- }
-}
diff --git a/internal/server/base/base.go b/internal/server/base/base.go
deleted file mode 100644
index d483dad..0000000
--- a/internal/server/base/base.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package base
-
-import (
- "time"
-
- "github.com/eduvpn/eduvpn-common/internal/http"
- "github.com/eduvpn/eduvpn-common/internal/server/endpoints"
- "github.com/eduvpn/eduvpn-common/internal/server/profile"
- "github.com/eduvpn/eduvpn-common/types/server"
-)
-
-// Base is the base type for servers.
-type Base struct {
- URL string `json:"base_url"`
- DisplayName map[string]string `json:"display_name"`
- SupportContact []string `json:"support_contact"`
- Endpoints endpoints.Endpoints `json:"endpoints"`
- Profiles profile.Info `json:"profiles"`
- StartTime time.Time `json:"start_time"`
- EndTime time.Time `json:"expire_time"`
- Type server.Type `json:"server_type"`
- HTTPClient *http.Client `json:"-"`
-}
-
-// RenewButtonTime returns the time when the renew button should be shown for the server
-// Implemented according to: https://github.com/eduvpn/documentation/blob/cdf4d054f7652d74e4192494e8bb0e21040e46ac/API.md#session-expiry
-func (b *Base) RenewButtonTime() int64 {
- d := b.EndTime.Sub(b.StartTime)
-
- // If the time is less than 24 hours (a day left), show it when 30 minutes have passed or on expired if less than 30 minutes
- dayl := time.Duration(24 * time.Hour)
- if d < dayl {
- // Get the minimum time to add, 30 minutes or on expired
- m := time.Duration(30 * time.Minute)
- // The total delta time is larger, return that we should show the button after 30 minutes
- if d > m {
- return b.StartTime.Add(30 * time.Minute).Unix()
- }
- // Just show it on expired
- return b.StartTime.Add(d).Unix()
- }
-
- // Else just show it when 24 hours is left
- // This is the delta minus 24 hours left as that's how long it takes for a day to be left in the expiry
- // We thus add this to the start time
- tillDay := d - dayl
- t := b.StartTime.Add(tillDay)
- return t.Unix()
-}
-
-func (b *Base) CountdownTime() int64 {
- d := b.EndTime.Sub(b.StartTime)
-
- dayl := time.Duration(24 * time.Hour)
-
- // This is just the last 24 hours
- // if less than or equal to 24 hours, immediately
- if d <= dayl {
- return b.StartTime.Unix()
- }
-
- tillDay := d - dayl
- t := b.StartTime.Add(tillDay)
- return t.Unix()
-}
-
-func (b *Base) NotificationTimes() []int64 {
- last := []time.Duration{
- time.Duration(0),
- time.Duration(1 * time.Hour),
- time.Duration(2 * time.Hour),
- time.Duration(4 * time.Hour),
- }
-
- var t []int64
-
- d := b.EndTime.Sub(b.StartTime)
- for _, l := range last {
- // If the notification remaining time is more than the total delta, continue
- if l > d {
- continue
- }
- // calculating the time till a notification must happen
- tillN := d - l
- // Get absolute time when this notification must be shown by adding the delta
- c := b.StartTime.Add(tillN)
- t = append(t, c.Unix())
- }
- return t
-}
diff --git a/internal/server/custom/custom.go b/internal/server/custom/custom.go
deleted file mode 100644
index 376bcd6..0000000
--- a/internal/server/custom/custom.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package custom
-
-import (
- "context"
- "net/url"
-
- "github.com/eduvpn/eduvpn-common/internal/server/api"
- "github.com/eduvpn/eduvpn-common/internal/server/base"
- "github.com/eduvpn/eduvpn-common/internal/server/institute"
- "github.com/eduvpn/eduvpn-common/types/server"
- "github.com/go-errors/errors"
-)
-
-type (
- Server = institute.Server
- Servers = institute.Servers
-)
-
-func New(ctx context.Context, clientID string, u string) (*Server, error) {
- pu, err := url.Parse(u)
- if err != nil {
- return nil, errors.WrapPrefix(err, "failed to parse custom server URL", 0)
- }
- b := base.Base{
- URL: u,
- DisplayName: map[string]string{"en": pu.Hostname()},
- Type: server.TypeCustom,
- }
- if err := api.Endpoints(ctx, &b); err != nil {
- return nil, err
- }
- API := b.Endpoints.API.V3
-
- s := &Server{Basic: b}
- // we set ISS to empty here as we do not want to have ISS enabled for custom servers
- // Otherwise we would have to normalise the URL which the user has entered which is error prone
- s.Auth.Init(clientID, "", API.Authorization, API.Token)
- return s, nil
-}
diff --git a/internal/server/endpoints/endpoints.go b/internal/server/endpoints/endpoints.go
deleted file mode 100644
index 3e675e4..0000000
--- a/internal/server/endpoints/endpoints.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package endpoints
-
-import (
- "net/url"
-
- "github.com/go-errors/errors"
-)
-
-type List struct {
- API string `json:"api_endpoint"`
- Authorization string `json:"authorization_endpoint"`
- Token string `json:"token_endpoint"`
-}
-
-type Versions struct {
- V2 List `json:"http://eduvpn.org/api#2"`
- V3 List `json:"http://eduvpn.org/api#3"`
-}
-
-// Endpoints defines the json format for /.well-known/vpn-user-portal".
-type Endpoints struct {
- API Versions `json:"api"`
- V string `json:"v"`
-}
-
-func (e Endpoints) Validate() error {
- v3 := e.API.V3
- pAPI, err := url.Parse(v3.API)
- if err != nil {
- return errors.WrapPrefix(err, "failed to parse API endpoint", 0)
- }
- pAuth, err := url.Parse(v3.Authorization)
- if err != nil {
- return errors.WrapPrefix(err, "failed to parse API authorization endpoint", 0)
- }
- pToken, err := url.Parse(v3.Token)
- if err != nil {
- return errors.WrapPrefix(err, "failed to parse API token endpoint", 0)
- }
- if pAPI.Scheme != "https" {
- return errors.Errorf("API Scheme: '%v', is not equal to HTTPS", pAPI.Scheme)
- }
- if pAPI.Scheme != pAuth.Scheme {
- return errors.Errorf("API scheme: '%v', is not equal to authorization scheme: '%v'", pAPI.Scheme, pAuth.Scheme)
- }
- if pAPI.Scheme != pToken.Scheme {
- return errors.Errorf("API scheme: '%v', is not equal to token scheme: '%v'", pAPI.Scheme, pToken.Scheme)
- }
- if pAPI.Host != pAuth.Host {
- return errors.Errorf("API host: '%v', is not equal to authorization host: '%v'", pAPI.Host, pAuth.Host)
- }
- if pAPI.Host != pToken.Host {
- return errors.Errorf("API host: '%v', is not equal to token host: '%v'", pAPI.Host, pToken.Host)
- }
- return nil
-}
diff --git a/internal/server/institute/institute.go b/internal/server/institute/institute.go
deleted file mode 100644
index 82e51e6..0000000
--- a/internal/server/institute/institute.go
+++ /dev/null
@@ -1,133 +0,0 @@
-package institute
-
-import (
- "context"
-
- "github.com/eduvpn/eduvpn-common/internal/discovery"
- "github.com/eduvpn/eduvpn-common/internal/oauth"
- "github.com/eduvpn/eduvpn-common/internal/server/api"
- "github.com/eduvpn/eduvpn-common/internal/server/base"
- "github.com/eduvpn/eduvpn-common/types/server"
- "github.com/go-errors/errors"
-)
-
-type Server struct {
- // An instute access server has its own OAuth
- Auth oauth.OAuth `json:"oauth"`
-
- // Embed the server base
- Basic base.Base `json:"base"`
-}
-
-type Servers struct {
- Map map[string]*Server `json:"map"`
- CurrentURL string `json:"current_url"`
-}
-
-func New(
- ctx context.Context,
- clientID string,
- url string,
- name map[string]string,
- supportContact []string,
-) (*Server, error) {
- b := base.Base{
- URL: url,
- DisplayName: name,
- SupportContact: supportContact,
- Type: server.TypeInstituteAccess,
- }
- if err := api.Endpoints(ctx, &b); err != nil {
- return nil, err
- }
- API := b.Endpoints.API.V3
-
- s := &Server{Basic: b}
-
- if url == "" {
- return nil, errors.New("URL passed to OAuth is empty")
- }
- s.Auth.Init(clientID, url, API.Authorization, API.Token)
- return s, nil
-}
-
-func (s *Servers) Current() (*Server, error) {
- if s.Map == nil {
- return nil, errors.Errorf("No map is found when getting the current server")
- }
-
- srv, ok := s.Map[s.CurrentURL]
- if !ok || srv == nil {
- return nil, errors.Errorf("server not found")
- }
- return srv, nil
-}
-
-func (s *Servers) Remove(url string) error {
- // check if it is in the map to begin with
- if _, ok := s.Map[url]; ok {
- delete(s.Map, url)
- } else {
- return errors.Errorf("cannot remove URL: %v, not found in list", url)
- }
-
- // Reset the current url
- if s.CurrentURL == url {
- s.CurrentURL = ""
- }
- return nil
-}
-
-func (s *Servers) Add(srv *Server) {
- if s.Map == nil {
- s.Map = make(map[string]*Server)
- }
- s.Map[srv.Basic.URL] = srv
-}
-
-func (s *Server) TemplateAuth() func(string) string {
- return func(authURL string) string {
- return authURL
- }
-}
-
-func (s *Server) Base() (*base.Base, error) {
- return &s.Basic, nil
-}
-
-func (s *Server) OAuth() *oauth.OAuth {
- return &s.Auth
-}
-
-func (s *Server) NeedsLocation() bool {
- return false
-}
-
-func (s *Server) RefreshEndpoints(ctx context.Context, _ *discovery.Discovery) error {
- // Re-initialize the endpoints
- b, err := s.Base()
- if err != nil {
- return err
- }
-
- err = api.Endpoints(ctx, b)
- if err != nil {
- return err
- }
-
- // update OAuth
- auth := s.OAuth()
- if auth != nil {
- auth.BaseAuthorizationURL = b.Endpoints.API.V3.Authorization
- auth.TokenURL = b.Endpoints.API.V3.Token
- }
- return nil
-}
-
-func (s *Server) Public() (interface{}, error) {
- return &server.Server{
- DisplayName: s.Basic.DisplayName,
- Identifier: s.Basic.URL,
- Profiles: s.Basic.Profiles.Public(),
- }, nil
-}
diff --git a/internal/server/list.go b/internal/server/list.go
deleted file mode 100644
index f3ae4e8..0000000
--- a/internal/server/list.go
+++ /dev/null
@@ -1,180 +0,0 @@
-package server
-
-import (
- "context"
-
- "github.com/eduvpn/eduvpn-common/internal/server/custom"
- "github.com/eduvpn/eduvpn-common/internal/server/institute"
- "github.com/eduvpn/eduvpn-common/internal/server/secure"
- discotypes "github.com/eduvpn/eduvpn-common/types/discovery"
- srvtypes "github.com/eduvpn/eduvpn-common/types/server"
- "github.com/go-errors/errors"
-)
-
-type List struct {
- CustomServers custom.Servers `json:"custom_servers"`
- InstituteServers institute.Servers `json:"institute_servers"`
- SecureInternetHomeServer secure.Server `json:"secure_internet_home"`
- IsType srvtypes.Type `json:"is_secure_internet"`
-}
-
-// HasSecureInternet returns whether or not we have a secure internet server added
-func (l *List) HasSecureInternet() bool {
- return len(l.SecureInternetHomeServer.BaseMap) > 0
-}
-
-func (l *List) HasSecureLocation() bool {
- return l.SecureInternetHomeServer.CurrentLocation != ""
-}
-
-func (l *List) Current() (Server, error) {
- if l.IsType == srvtypes.TypeUnknown {
- return nil, errors.New("no current server")
- }
- if l.IsType == srvtypes.TypeSecureInternet {
- if !l.HasSecureLocation() {
- return nil, errors.Errorf("Current server is secure internet but there is no secure internet location: %v", l.IsType)
- }
- return &l.SecureInternetHomeServer, nil
- }
-
- if l.IsType == srvtypes.TypeCustom {
- return l.CustomServers.Current()
- }
- return l.InstituteServers.Current()
-}
-
-func (l *List) AddCustom(ctx context.Context, clientID string, url string) (Server, error) {
- srv, err := custom.New(ctx, clientID, url)
- if err != nil {
- return nil, err
- }
- l.CustomServers.Add(srv)
- return srv, nil
-}
-
-func (l *List) AddInstituteAccess(ctx context.Context, clientID string, discoServer *discotypes.Server) (Server, error) {
- srv, err := institute.New(ctx, clientID, discoServer.BaseURL, discoServer.DisplayName, discoServer.SupportContact)
- if err != nil {
- return nil, err
- }
- l.InstituteServers.Add(srv)
- return srv, nil
-}
-
-func (l *List) AddSecureInternet(
- ctx context.Context,
- clientID string,
- secureOrg *discotypes.Organization,
- secureServer *discotypes.Server,
-) (*secure.Server, error) {
- // If we have specified an organization ID
- // We also need to get an authorization template
- err := l.SecureInternetHomeServer.Init(ctx, clientID, secureOrg, secureServer)
- if err != nil {
- return nil, err
- }
-
- l.IsType = srvtypes.TypeSecureInternet
- return &l.SecureInternetHomeServer, nil
-}
-
-func (l *List) SecureInternet(identifier string) (*secure.Server, error) {
- if l.SecureInternetHomeServer.HomeOrganizationID != identifier {
- return nil, errors.Errorf("no secure internet home server with identifier: %s", identifier)
- }
- return &l.SecureInternetHomeServer, nil
-}
-
-func (l *List) SetSecureInternet(server Server) error {
- b, err := server.Base()
- if err != nil {
- return err
- }
-
- if b.Type != srvtypes.TypeSecureInternet {
- return errors.New("not a secure internet server")
- }
-
- // The location should already be configured
- // TODO: check for location?
- l.IsType = srvtypes.TypeSecureInternet
- return nil
-}
-
-func (l *List) RemoveSecureInternet(identifier string) error {
- oid := l.SecureInternetHomeServer.HomeOrganizationID
- if identifier != oid {
- return errors.Errorf("cannot remove secure internet server: identifier: %s, is not equal to the Org ID: %s", identifier, oid)
- }
- // Empty out the struct
- l.SecureInternetHomeServer = secure.Server{}
-
- // If the current server is secure internet, reset to unknown
- if l.IsType == srvtypes.TypeSecureInternet {
- l.IsType = srvtypes.TypeUnknown
- }
- return nil
-}
-
-func (l *List) SetInstituteAccess(srv Server) error {
- b, err := srv.Base()
- if err != nil {
- return err
- }
-
- if b.Type != srvtypes.TypeInstituteAccess {
- return errors.Errorf("not an institute access server, URL: %s, type: %v", b.URL, b.Type)
- }
-
- if _, ok := l.InstituteServers.Map[b.URL]; ok {
- l.InstituteServers.CurrentURL = b.URL
- l.IsType = srvtypes.TypeInstituteAccess
- } else {
- return errors.Errorf("institute access server with URL: %s, is not yet configured", b.URL)
- }
- return nil
-}
-
-func (l *List) InstituteAccess(url string) (*institute.Server, error) {
- if srv, ok := l.InstituteServers.Map[url]; ok {
- return srv, nil
- }
- return nil, errors.Errorf("no institute access server with URL: %s", url)
-}
-
-func (l *List) RemoveInstituteAccess(url string) error {
- // TODO: Reset current to unknown?
- return l.InstituteServers.Remove(url)
-}
-
-func (l *List) SetCustom(server Server) error {
- b, err := server.Base()
- if err != nil {
- return err
- }
-
- if b.Type != srvtypes.TypeCustom {
- return errors.New("not a custom server")
- }
-
- if _, ok := l.CustomServers.Map[b.URL]; ok {
- l.CustomServers.CurrentURL = b.URL
- l.IsType = srvtypes.TypeCustom
- } else {
- return errors.Errorf("this server is not yet added as a custom server: %s", b.URL)
- }
- return nil
-}
-
-func (l *List) CustomServer(url string) (*institute.Server, error) {
- if srv, ok := l.CustomServers.Map[url]; ok {
- return srv, nil
- }
- return nil, errors.Errorf("failed to get institute access server - no custom server with URL '%s'", url)
-}
-
-func (l *List) RemoveCustom(url string) error {
- // TODO: Reset current to unknown?
- return l.CustomServers.Remove(url)
-}
diff --git a/internal/server/profile/profile.go b/internal/server/profile/profile.go
deleted file mode 100644
index 7a19685..0000000
--- a/internal/server/profile/profile.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package profile
-
-import (
- "github.com/eduvpn/eduvpn-common/types/protocol"
- "github.com/eduvpn/eduvpn-common/types/server"
-)
-
-type Profile struct {
- ID string `json:"profile_id"`
- DisplayName string `json:"display_name"`
- VPNProtoList []string `json:"vpn_proto_list"`
- DefaultGateway bool `json:"default_gateway"`
-}
-
-type ListInfo struct {
- ProfileList []Profile `json:"profile_list"`
-}
-
-type Info struct {
- Current string `json:"current_profile"`
- Info ListInfo `json:"info"`
-}
-
-func (info Info) CurrentProfileIndex() int {
- for i, profile := range info.Info.ProfileList {
- if profile.ID == info.Current {
- return i
- }
- }
- // Default is 'first' profile
- return 0
-}
-
-func (profile *Profile) supportsProtocol(protocol string) bool {
- for _, proto := range profile.VPNProtoList {
- if proto == protocol {
- return true
- }
- }
- return false
-}
-
-func (profile *Profile) SupportsWireguard() bool {
- return profile.supportsProtocol("wireguard")
-}
-
-func (profile *Profile) SupportsOpenVPN() bool {
- return profile.supportsProtocol("openvpn")
-}
-
-func (info Info) Supported(wireguardSupport bool) []Profile {
- var valid []Profile
- for _, p := range info.Info.ProfileList {
- // Not a valid profile because it does not support openvpn
- // Also the client does not support wireguard
- if !p.SupportsOpenVPN() && !wireguardSupport {
- continue
- }
- valid = append(valid, p)
- }
- return valid
-}
-
-func (info Info) Has(id string) bool {
- for _, p := range info.Info.ProfileList {
- if p.ID == id {
- return true
- }
- }
- return false
-}
-
-func (info Info) Public() server.Profiles {
- m := make(map[string]server.Profile)
- for _, p := range info.Info.ProfileList {
- var protocols []protocol.Protocol
- for _, ps := range p.VPNProtoList {
- protocols = append(protocols, protocol.New(ps))
- }
- m[p.ID] = server.Profile{
- DisplayName: map[string]string{
- "en": p.DisplayName,
- },
- Protocols: protocols,
- }
- }
- return server.Profiles{Map: m, Current: info.Current}
-}
diff --git a/internal/server/profile/profile_test.go b/internal/server/profile/profile_test.go
deleted file mode 100644
index e246b5c..0000000
--- a/internal/server/profile/profile_test.go
+++ /dev/null
@@ -1,100 +0,0 @@
-package profile
-
-import "testing"
-
-func Test_CurrentProfileIndex(t *testing.T) {
- testCases := []struct {
- profiles []Profile
- current string
- index int
- }{
- {
- profiles: []Profile{
- {
- ID: "a",
- DisplayName: "b",
- VPNProtoList: []string{"openvpn", "wireguard"},
- },
- },
- current: "a",
- index: 0,
- },
- {
- profiles: []Profile{
- {
- ID: "a",
- DisplayName: "a",
- VPNProtoList: []string{"openvpn", "wireguard"},
- },
- {
- ID: "b",
- DisplayName: "b",
- VPNProtoList: []string{"openvpn", "wireguard"},
- },
- },
- current: "b",
- index: 1,
- },
- {
- profiles: []Profile{
- {
- ID: "a",
- DisplayName: "a",
- VPNProtoList: []string{"openvpn", "wireguard"},
- },
- {
- ID: "b",
- DisplayName: "b",
- VPNProtoList: []string{"openvpn", "wireguard"},
- },
- },
- current: "",
- index: 0,
- },
- {
- profiles: []Profile{
- {
- ID: "a",
- DisplayName: "a",
- VPNProtoList: []string{"openvpn", "wireguard"},
- },
- {
- ID: "b",
- DisplayName: "b",
- VPNProtoList: []string{"openvpn", "wireguard"},
- },
- },
- current: "",
- index: 0,
- },
- {
- profiles: []Profile{
- {
- ID: "a",
- DisplayName: "a",
- VPNProtoList: []string{"openvpn", "wireguard"},
- },
- {
- ID: "b",
- DisplayName: "b",
- VPNProtoList: []string{"openvpn", "wireguard"},
- },
- },
- current: "idonotexist",
- index: 0,
- },
- }
-
- for _, tc := range testCases {
- pri := &Info{
- Current: tc.current,
- Info: ListInfo{
- ProfileList: tc.profiles,
- },
- }
- got := pri.CurrentProfileIndex()
- if got != tc.index {
- t.Fatalf("failed getting profile index, got: '%v', want: '%v'", got, tc.index)
- }
- }
-}
diff --git a/internal/server/secure/secure.go b/internal/server/secure/secure.go
deleted file mode 100644
index 16479fa..0000000
--- a/internal/server/secure/secure.go
+++ /dev/null
@@ -1,185 +0,0 @@
-package secure
-
-import (
- "context"
- "github.com/eduvpn/eduvpn-common/internal/discovery"
- "github.com/eduvpn/eduvpn-common/internal/oauth"
- "github.com/eduvpn/eduvpn-common/internal/server/api"
- "github.com/eduvpn/eduvpn-common/internal/server/base"
- "github.com/eduvpn/eduvpn-common/internal/util"
- discotypes "github.com/eduvpn/eduvpn-common/types/discovery"
- "github.com/eduvpn/eduvpn-common/types/server"
- "github.com/go-errors/errors"
-)
-
-// Server secure internet server which has its own OAuth tokens
-// It specifies the current location url it is connected to.
-type Server struct {
- Auth oauth.OAuth `json:"oauth"`
- DisplayName map[string]string `json:"display_name"`
-
- // The home server has a list of info for each configured server location
- BaseMap map[string]*base.Base `json:"base_map"`
-
- // We have the authorization URL template, the home organization ID and the current location
- AuthorizationTemplate string `json:"authorization_template"`
- HomeOrganizationID string `json:"home_organization_id"`
- CurrentLocation string `json:"current_location"`
-}
-
-func (s *Server) TemplateAuth() func(string) string {
- return func(authURL string) string {
- return util.ReplaceWAYF(s.AuthorizationTemplate, authURL, s.HomeOrganizationID)
- }
-}
-
-func (s *Server) Base() (*base.Base, error) {
- if s.BaseMap == nil {
- return nil, errors.Errorf("secure internet map not found")
- }
-
- b, ok := s.BaseMap[s.CurrentLocation]
- if !ok {
- return nil, errors.Errorf("secure internet base with location '%s' not found", s.CurrentLocation)
- }
- return b, nil
-}
-
-func (s *Server) OAuth() *oauth.OAuth {
- return &s.Auth
-}
-
-func (s *Server) NeedsLocation() bool {
- if s.CurrentLocation == "" {
- return true
- }
- if len(s.BaseMap) == 0 {
- return true
- }
- return false
-}
-
-func (s *Server) RefreshEndpoints(ctx context.Context, disco *discovery.Discovery) error {
- // update OAuth for home server
- auth := s.OAuth()
- if auth != nil && s.HomeOrganizationID != "" {
- _, srv, err := disco.SecureHomeArgs(s.HomeOrganizationID)
- if err != nil {
- return err
- }
- if hb, ok := s.BaseMap[srv.CountryCode]; ok && hb != nil {
- err := api.Endpoints(ctx, hb)
- if err != nil {
- return err
- }
- auth.BaseAuthorizationURL = hb.Endpoints.API.V3.Authorization
- auth.TokenURL = hb.Endpoints.API.V3.Token
- }
- // already updated, return
- if srv.CountryCode == s.CurrentLocation {
- return nil
- }
- }
-
- // refresh the current location endpoints
- // Re-initialize the endpoints
- b, err := s.Base()
- if err != nil {
- return err
- }
-
- err = api.Endpoints(ctx, b)
- if err != nil {
- return err
- }
- return nil
-}
-
-func (s *Server) addLocation(ctx context.Context, locSrv *discotypes.Server) (*base.Base, error) {
- // Initialize the base map if it is non-nil
- if s.BaseMap == nil {
- s.BaseMap = make(map[string]*base.Base)
- }
-
- // Add the location to the base map
- b, ok := s.BaseMap[locSrv.CountryCode]
- if !ok || b == nil {
- // Create the base to be added to the map
- b = &base.Base{}
- b.URL = locSrv.BaseURL
- b.DisplayName = s.DisplayName
- b.SupportContact = locSrv.SupportContact
- b.Type = server.TypeSecureInternet
- if err := api.Endpoints(ctx, b); err != nil {
- return nil, err
- }
- }
-
- // Ensure it is in the map
- s.BaseMap[locSrv.CountryCode] = b
- return b, nil
-}
-
-func (s *Server) Location(ctx context.Context, locSrv *discotypes.Server) error {
- if _, err := s.addLocation(ctx, locSrv); err != nil {
- return err
- }
- s.CurrentLocation = locSrv.CountryCode
- return nil
-}
-
-// Initializes the home server and adds its own location.
-func (s *Server) Init(
- ctx context.Context,
- clientID string,
- homeOrg *discotypes.Organization, homeLoc *discotypes.Server,
-) error {
- if s.HomeOrganizationID != homeOrg.OrgID {
- // New home organisation, clear everything
- *s = Server{}
- }
-
- // Make sure to set the organization ID
- s.HomeOrganizationID = homeOrg.OrgID
- s.DisplayName = homeOrg.DisplayName
-
- // Make sure to set the authorization URL template
- s.AuthorizationTemplate = homeLoc.AuthenticationURLTemplate
-
- b, err := s.addLocation(ctx, homeLoc)
- if err != nil {
- return err
- }
-
- // set the home location as the current
- err = s.Location(ctx, homeLoc)
- if err != nil {
- return err
- }
-
- if b.URL == "" {
- return errors.New("URL passed to OAuth is empty")
- }
-
- // Make sure oauth contains our endpoints
- s.Auth.Init(clientID, b.URL, b.Endpoints.API.V3.Authorization, b.Endpoints.API.V3.Token)
- return nil
-}
-
-func (s *Server) Public() (interface{}, error) {
- b, err := s.Base()
- var p server.Profiles
- dn := s.DisplayName
- if err == nil {
- dn = b.DisplayName
- p = b.Profiles.Public()
- }
- return &server.SecureInternet{
- Server: server.Server{
- DisplayName: dn,
- Identifier: s.HomeOrganizationID,
- Profiles: p,
- },
- CountryCode: s.CurrentLocation,
- }, nil
-}