diff --git a/backend/jottacloud/jottacloud.go b/backend/jottacloud/jottacloud.go index e77515a60..e54e0a520 100644 --- a/backend/jottacloud/jottacloud.go +++ b/backend/jottacloud/jottacloud.go @@ -17,6 +17,7 @@ import ( "net/url" "os" "path" + "sort" "strconv" "strings" "time" @@ -66,24 +67,26 @@ const ( legacyClientID = "nibfk8biu12ju7hpqomr8b1e40" legacyEncryptedClientSecret = "Vp8eAv7eVElMnQwN-kgU9cbhgApNDaMqWdlDi5qFydlQoji4JBxrGMF2" legacyConfigVersion = 0 - - teliaseCloudTokenURL = "https://cloud-auth.telia.se/auth/realms/telia_se/protocol/openid-connect/token" - teliaseCloudAuthURL = "https://cloud-auth.telia.se/auth/realms/telia_se/protocol/openid-connect/auth" - teliaseCloudClientID = "desktop" - - telianoCloudTokenURL = "https://sky-auth.telia.no/auth/realms/get/protocol/openid-connect/token" - telianoCloudAuthURL = "https://sky-auth.telia.no/auth/realms/get/protocol/openid-connect/auth" - telianoCloudClientID = "desktop" - - tele2CloudTokenURL = "https://mittcloud-auth.tele2.se/auth/realms/comhem/protocol/openid-connect/token" - tele2CloudAuthURL = "https://mittcloud-auth.tele2.se/auth/realms/comhem/protocol/openid-connect/auth" - tele2CloudClientID = "desktop" - - onlimeCloudTokenURL = "https://cloud-auth.onlime.dk/auth/realms/onlime_wl/protocol/openid-connect/token" - onlimeCloudAuthURL = "https://cloud-auth.onlime.dk/auth/realms/onlime_wl/protocol/openid-connect/auth" - onlimeCloudClientID = "desktop" ) +func getWhitelabelServices() map[string]struct { + name, domain, realm, clientID string + scopes []string +} { + return map[string]struct { + name string + domain string + realm string + clientID string + scopes []string + }{ + "telia_se": {"Telia Cloud (Sweden)", "cloud-auth.telia.se", "telia_se", "desktop", []string{"openid", "jotta-default", "offline_access"}}, + "telia_no": {"Telia Sky (Norway)", "sky-auth.telia.no", "get", "desktop", []string{"openid", "jotta-default", "offline_access"}}, + "tele2": {"Tele2 Cloud (Sweden)", "mittcloud-auth.tele2.se", "comhem", "desktop", []string{"openid", "jotta-default", "offline_access"}}, + "onlime": {"Onlime (Denmark)", "cloud-auth.onlime.dk", "onlime_wl", "desktop", []string{"openid", "jotta-default", "offline_access"}}, + } +} + // Register with Fs func init() { // needs to be done early so we can use oauth during config @@ -165,21 +168,12 @@ func Config(ctx context.Context, name string, m configmap.Mapper, config fs.Conf return fs.ConfigChooseExclusiveFixed("auth_type_done", "config_type", `Select authentication type.`, []fs.OptionExample{{ Value: "standard", Help: "Standard authentication.\nUse this if you're a normal Jottacloud user.", + }, { + Value: "whitelabel", + Help: "Whitelabel authentication.\nUse this if you are using the service offered by a third party such as Telia, Tele2, Onlime, Elkjøp, etc.", }, { Value: "legacy", - Help: "Legacy authentication.\nThis is only required for certain whitelabel versions of Jottacloud and not recommended for normal users.", - }, { - Value: "telia_se", - Help: "Telia Cloud authentication.\nUse this if you are using Telia Cloud (Sweden).", - }, { - Value: "telia_no", - Help: "Telia Sky authentication.\nUse this if you are using Telia Sky (Norway).", - }, { - Value: "tele2", - Help: "Tele2 Cloud authentication.\nUse this if you are using Tele2 Cloud.", - }, { - Value: "onlime", - Help: "Onlime Cloud authentication.\nUse this if you are using Onlime Cloud.", + Help: "Legacy authentication.\nThis is no longer supported by any known services and not recommended for normal users.", }}) case "auth_type_done": // Jump to next state according to config chosen @@ -203,6 +197,48 @@ func Config(ctx context.Context, name string, m configmap.Mapper, config fs.Conf return nil, fmt.Errorf("error while saving token: %w", err) } return fs.ConfigGoto("choose_device") + case "whitelabel": + whitelabels := getWhitelabelServices() + options := make([]fs.OptionExample, 0, len(whitelabels)) + for key, val := range whitelabels { + options = append(options, fs.OptionExample{ + Value: key, + Help: val.name, + }) + } + sort.Slice(options, func(i, j int) bool { + return options[i].Help < options[j].Help + }) + return fs.ConfigChooseExclusiveFixed("whitelabel_type", "config_whitelabel", + "White-label service. This decides the domain name to connect to and\nthe authentication configuration to use.", + options) + case "whitelabel_type": + whitelabel, ok := getWhitelabelServices()[config.Result] + if !ok { + return nil, fmt.Errorf("unexpected whitelabel %q", config.Result) + } + opts := rest.Opts{ + Method: "GET", + RootURL: "https://" + whitelabel.domain + "/auth/realms/" + whitelabel.realm + "/.well-known/openid-configuration", + } + var wellKnown api.WellKnown + srv := rest.NewClient(fshttp.NewClient(ctx)) + _, err := srv.CallJSON(ctx, &opts, nil, &wellKnown) + if err != nil { + return nil, fmt.Errorf("failed to get authentication provider configuration: %w", err) + } + m.Set("configVersion", fmt.Sprint(configVersion)) + m.Set(configClientID, whitelabel.clientID) + m.Set(configTokenURL, wellKnown.TokenEndpoint) + return oauthutil.ConfigOut("choose_device", &oauthutil.Options{ + OAuth2Config: &oauthutil.Config{ + AuthURL: wellKnown.AuthorizationEndpoint, + TokenURL: wellKnown.TokenEndpoint, + ClientID: whitelabel.clientID, + Scopes: whitelabel.scopes, + RedirectURL: oauthutil.RedirectLocalhostURL, + }, + }) case "legacy": // configure a jottacloud backend using legacy authentication m.Set("configVersion", fmt.Sprint(legacyConfigVersion)) return fs.ConfigConfirm("legacy_api", false, "config_machine_specific", `Do you want to create a machine specific API key? @@ -272,58 +308,6 @@ machines.`) return nil, fmt.Errorf("error while saving token: %w", err) } return fs.ConfigGoto("choose_device") - case "telia_se": // telia_se cloud config - m.Set("configVersion", fmt.Sprint(configVersion)) - m.Set(configClientID, teliaseCloudClientID) - m.Set(configTokenURL, teliaseCloudTokenURL) - return oauthutil.ConfigOut("choose_device", &oauthutil.Options{ - OAuth2Config: &oauthutil.Config{ - AuthURL: teliaseCloudAuthURL, - TokenURL: teliaseCloudTokenURL, - ClientID: teliaseCloudClientID, - Scopes: []string{"openid", "jotta-default", "offline_access"}, - RedirectURL: oauthutil.RedirectLocalhostURL, - }, - }) - case "telia_no": // telia_no cloud config - m.Set("configVersion", fmt.Sprint(configVersion)) - m.Set(configClientID, telianoCloudClientID) - m.Set(configTokenURL, telianoCloudTokenURL) - return oauthutil.ConfigOut("choose_device", &oauthutil.Options{ - OAuth2Config: &oauthutil.Config{ - AuthURL: telianoCloudAuthURL, - TokenURL: telianoCloudTokenURL, - ClientID: telianoCloudClientID, - Scopes: []string{"openid", "jotta-default", "offline_access"}, - RedirectURL: oauthutil.RedirectLocalhostURL, - }, - }) - case "tele2": // tele2 cloud config - m.Set("configVersion", fmt.Sprint(configVersion)) - m.Set(configClientID, tele2CloudClientID) - m.Set(configTokenURL, tele2CloudTokenURL) - return oauthutil.ConfigOut("choose_device", &oauthutil.Options{ - OAuth2Config: &oauthutil.Config{ - AuthURL: tele2CloudAuthURL, - TokenURL: tele2CloudTokenURL, - ClientID: tele2CloudClientID, - Scopes: []string{"openid", "jotta-default", "offline_access"}, - RedirectURL: oauthutil.RedirectLocalhostURL, - }, - }) - case "onlime": // onlime cloud config - m.Set("configVersion", fmt.Sprint(configVersion)) - m.Set(configClientID, onlimeCloudClientID) - m.Set(configTokenURL, onlimeCloudTokenURL) - return oauthutil.ConfigOut("choose_device", &oauthutil.Options{ - OAuth2Config: &oauthutil.Config{ - AuthURL: onlimeCloudAuthURL, - TokenURL: onlimeCloudTokenURL, - ClientID: onlimeCloudClientID, - Scopes: []string{"openid", "jotta-default", "offline_access"}, - RedirectURL: oauthutil.RedirectLocalhostURL, - }, - }) case "choose_device": return fs.ConfigConfirm("choose_device_query", false, "config_non_standard", `Use a non-standard device/mountpoint? Choosing no, the default, will let you access the storage used for the archive