From 819d7f9914cbb34abb76b932c05b030a34986ec2 Mon Sep 17 00:00:00 2001 From: jwijenbergh Date: Tue, 6 Feb 2024 16:26:05 +0100 Subject: Server: Delete old files to prepare for new internal API --- internal/server/api/api.go | 217 -------------------------------- internal/server/api/api_test.go | 150 ---------------------- internal/server/base/base.go | 90 ------------- internal/server/custom/custom.go | 39 ------ internal/server/endpoints/endpoints.go | 56 --------- internal/server/institute/institute.go | 133 -------------------- internal/server/list.go | 180 -------------------------- internal/server/profile/profile.go | 88 ------------- internal/server/profile/profile_test.go | 100 --------------- internal/server/secure/secure.go | 185 --------------------------- 10 files changed, 1238 deletions(-) delete mode 100644 internal/server/api/api.go delete mode 100644 internal/server/api/api_test.go delete mode 100644 internal/server/base/base.go delete mode 100644 internal/server/custom/custom.go delete mode 100644 internal/server/endpoints/endpoints.go delete mode 100644 internal/server/institute/institute.go delete mode 100644 internal/server/list.go delete mode 100644 internal/server/profile/profile.go delete mode 100644 internal/server/profile/profile_test.go delete mode 100644 internal/server/secure/secure.go (limited to 'internal/server') 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 -} -- cgit v1.2.3