package server import ( "context" "errors" "log/slog" "net/url" "strings" "time" "herkulessi.de/git/eduvpn-common/internal/config/v2" "herkulessi.de/git/eduvpn-common/internal/discovery" "herkulessi.de/git/eduvpn-common/internal/eduvpnapi" "herkulessi.de/git/eduvpn-common/types/server" "codeberg.org/jwijenbergh/eduoauth-go/v2" ) // ReplaceWAYF replaces an authorization template containing of @RETURN_TO@ and @ORG_ID@ with the authorization URL and the organization ID // See https://github.com/eduvpn/documentation/blob/dc4d53c47dd7a69e95d6650eec408e16eaa814a2/SERVER_DISCOVERY_SKIP_WAYF.md func ReplaceWAYF(template string, authURL string, orgID string) string { // We just return the authURL in the cases where the template is not given or is invalid if template == "" { return authURL } if !strings.Contains(template, "@RETURN_TO@") { return authURL } if !strings.Contains(template, "@ORG_ID@") { return authURL } // Replace authURL template = strings.Replace(template, "@RETURN_TO@", url.QueryEscape(authURL), 1) // If now there is no more ORG_ID, return as there weren't enough @ symbols if !strings.Contains(template, "@ORG_ID@") { return authURL } // Replace ORG ID template = strings.Replace(template, "@ORG_ID@", url.QueryEscape(orgID), 1) return template } // AddSecure adds a secure internet server // `ctx` is the context used for cancellation // `disco` are the discovery servers // `orgID` is the organiztaion ID // `ot` specifies specifies the start time OAuth was already triggered func (s *Servers) AddSecure(ctx context.Context, disco *discovery.Discovery, orgID string, ot *int64) error { if s.config.HasSecureInternet() { return errors.New("a secure internet server already exists") } dorg, dsrv, err := disco.SecureHomeArgs(orgID) if err != nil { return err } sd := eduvpnapi.ServerData{ ID: dorg.OrgID, Type: server.TypeSecureInternet, BaseWK: dsrv.BaseURL, BaseAuthWK: dsrv.BaseURL, ProcessAuth: func(ctx context.Context, url string) (string, error) { // the only thing we can do is log warn // this is already done in the functions disco.Servers(ctx, false) //nolint:errcheck disco.Organizations(ctx, false) //nolint:errcheck updorg, updsrv, err := disco.SecureHomeArgs(orgID) if err != nil { return "", err } ret := ReplaceWAYF(updsrv.AuthenticationURLTemplate, url, updorg.OrgID) return ret, nil }, } auth := time.Time{} if ot != nil { auth = time.Unix(*ot, 0) } err = s.config.AddServer(orgID, server.TypeSecureInternet, v2.Server{ CountryCode: dsrv.CountryCode, LastAuthorizeTime: auth, }) if err != nil { return err } // no authorization should be triggered, return if ot != nil { return nil } // Authorize by creating the API object _, err = eduvpnapi.NewAPI(ctx, s.clientID, sd, s.cb, nil) if err != nil { // authorization has failed, remove the server again rerr := s.config.RemoveServer(orgID, server.TypeSecureInternet) if rerr != nil { slog.Warn("could not remove secure internet server after failing authorization", "server", orgID, "error", rerr) } return err } return nil } // GetSecure gets a secure internet server // `ctx` is the context used for cancellation // `orgID` is the organization ID that identifies the server // `disco` are the discovery servers // `tok` are the tokens such that the server can be found without triggering auth // `disableAuth` is set to true when authorization should not be triggered func (s *Servers) GetSecure(ctx context.Context, orgID string, disco *discovery.Discovery, tok *eduoauth.Token, disableAuth bool) (*Server, error) { srv, err := s.config.GetServer(orgID, server.TypeSecureInternet) if err != nil { return nil, err } dorg, dhome, err := disco.SecureHomeArgs(orgID) if err != nil { return nil, err } dloc, err := disco.ServerByCountryCode(srv.CountryCode) if err != nil { return nil, err } sd := eduvpnapi.ServerData{ ID: dorg.OrgID, Type: server.TypeSecureInternet, BaseWK: dloc.BaseURL, BaseAuthWK: dhome.BaseURL, ProcessAuth: func(ctx context.Context, url string) (string, error) { // the only thing we can do is log warn // this is already done in the functions disco.MarkServersExpired() disco.Servers(ctx, false) //nolint:errcheck disco.MarkOrganizationsExpired() disco.Organizations(ctx, false) //nolint:errcheck updorg, updsrv, err := disco.SecureHomeArgs(orgID) if err != nil { return "", err } ret := ReplaceWAYF(updsrv.AuthenticationURLTemplate, url, updorg.OrgID) return ret, nil }, DisableAuthorize: disableAuth, } a, err := eduvpnapi.NewAPI(ctx, s.clientID, sd, s.cb, tok) if err != nil { return nil, err } sec := s.NewServer(orgID, server.TypeSecureInternet, a) return &sec, nil }