Instead of having support for a handful of SSO providers on our end, the user is always redirected to the default SSO URL where they will be presented with all the providers supported by the homeserver. This matches the UX of OAuth 2.0 where we can only redirect the user to the homeserver's UI, and allows us to remove a bunch of code and files.merge-requests/1714/merge
|
Before Width: | Height: | Size: 769 B |
|
Before Width: | Height: | Size: 772 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 889 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 254 B |
|
Before Width: | Height: | Size: 254 B |
@ -1,10 +0,0 @@ |
|||||||
using Gtk 4.0; |
|
||||||
|
|
||||||
template $SsoIdpButton: Gtk.Button { |
|
||||||
action-name: "login.sso"; |
|
||||||
|
|
||||||
styles [ |
|
||||||
"card", |
|
||||||
"sso-button", |
|
||||||
] |
|
||||||
} |
|
||||||
@ -1,183 +0,0 @@ |
|||||||
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*}; |
|
||||||
use matrix_sdk::ruma::api::client::session::get_login_types::v3::{ |
|
||||||
IdentityProvider, IdentityProviderBrand, |
|
||||||
}; |
|
||||||
|
|
||||||
use crate::gettext_f; |
|
||||||
|
|
||||||
mod imp { |
|
||||||
use std::{cell::OnceCell, marker::PhantomData}; |
|
||||||
|
|
||||||
use glib::subclass::InitializingObject; |
|
||||||
|
|
||||||
use super::*; |
|
||||||
|
|
||||||
#[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)] |
|
||||||
#[template(resource = "/org/gnome/Fractal/ui/login/sso_idp_button.ui")] |
|
||||||
#[properties(wrapper_type = super::SsoIdpButton)] |
|
||||||
pub struct SsoIdpButton { |
|
||||||
/// The identity provider of this button.
|
|
||||||
identity_provider: OnceCell<IdentityProvider>, |
|
||||||
/// The ID of the identity provider.
|
|
||||||
#[property(get = Self::id)] |
|
||||||
id: PhantomData<String>, |
|
||||||
/// The name of the identity provider.
|
|
||||||
#[property(get = Self::name)] |
|
||||||
name: PhantomData<String>, |
|
||||||
/// The brand of the identity provider, as a string.
|
|
||||||
#[property(get = Self::brand_string)] |
|
||||||
brand_string: PhantomData<String>, |
|
||||||
} |
|
||||||
|
|
||||||
#[glib::object_subclass] |
|
||||||
impl ObjectSubclass for SsoIdpButton { |
|
||||||
const NAME: &'static str = "SsoIdpButton"; |
|
||||||
type Type = super::SsoIdpButton; |
|
||||||
type ParentType = gtk::Button; |
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) { |
|
||||||
Self::bind_template(klass); |
|
||||||
|
|
||||||
klass.set_accessible_role(gtk::AccessibleRole::Button); |
|
||||||
} |
|
||||||
|
|
||||||
fn instance_init(obj: &InitializingObject<Self>) { |
|
||||||
obj.init_template(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[glib::derived_properties] |
|
||||||
impl ObjectImpl for SsoIdpButton {} |
|
||||||
|
|
||||||
impl WidgetImpl for SsoIdpButton {} |
|
||||||
impl ButtonImpl for SsoIdpButton {} |
|
||||||
|
|
||||||
impl SsoIdpButton { |
|
||||||
/// Set the identity provider of this button.
|
|
||||||
pub(super) fn set_identity_provider(&self, identity_provider: IdentityProvider) { |
|
||||||
let identity_provider = self.identity_provider.get_or_init(|| identity_provider); |
|
||||||
|
|
||||||
adw::StyleManager::default().connect_dark_notify(clone!( |
|
||||||
#[weak(rename_to = imp)] |
|
||||||
self, |
|
||||||
move |_| imp.update_icon() |
|
||||||
)); |
|
||||||
self.update_icon(); |
|
||||||
|
|
||||||
self.obj() |
|
||||||
.set_action_target_value(Some(&Some(&identity_provider.id).to_variant())); |
|
||||||
self.obj().set_tooltip_text(Some(&gettext_f( |
|
||||||
// Translators: Do NOT translate the content between '{' and '}', this is a
|
|
||||||
// variable name.
|
|
||||||
// This is the tooltip text on buttons to log in via Single Sign-On.
|
|
||||||
// The brand is something like Facebook, Apple, GitHub…
|
|
||||||
"Log in with {brand}", |
|
||||||
&[("brand", &identity_provider.name)], |
|
||||||
))); |
|
||||||
} |
|
||||||
|
|
||||||
/// The identity provider of this button.
|
|
||||||
fn identity_provider(&self) -> &IdentityProvider { |
|
||||||
self.identity_provider |
|
||||||
.get() |
|
||||||
.expect("identity provider is initialized") |
|
||||||
} |
|
||||||
|
|
||||||
/// The ID of the identity provider.
|
|
||||||
fn id(&self) -> String { |
|
||||||
self.identity_provider().id.clone() |
|
||||||
} |
|
||||||
|
|
||||||
/// The name of the identity provider.
|
|
||||||
fn name(&self) -> String { |
|
||||||
self.identity_provider().name.clone() |
|
||||||
} |
|
||||||
|
|
||||||
/// The brand of the identity provider.
|
|
||||||
fn brand(&self) -> &IdentityProviderBrand { |
|
||||||
self.identity_provider() |
|
||||||
.brand |
|
||||||
.as_ref() |
|
||||||
.expect("identity provider has a brand") |
|
||||||
} |
|
||||||
|
|
||||||
/// The brand of the identity provider, as a string.
|
|
||||||
fn brand_string(&self) -> String { |
|
||||||
self.brand().to_string() |
|
||||||
} |
|
||||||
|
|
||||||
/// The icon name of the brand, according to the current theme.
|
|
||||||
fn brand_icon(&self) -> &str { |
|
||||||
let is_dark = adw::StyleManager::default().is_dark(); |
|
||||||
|
|
||||||
match self.brand() { |
|
||||||
IdentityProviderBrand::Apple => { |
|
||||||
if is_dark { |
|
||||||
"idp-apple-dark" |
|
||||||
} else { |
|
||||||
"idp-apple" |
|
||||||
} |
|
||||||
} |
|
||||||
IdentityProviderBrand::Facebook => "idp-facebook", |
|
||||||
IdentityProviderBrand::GitHub => { |
|
||||||
if is_dark { |
|
||||||
"idp-github-dark" |
|
||||||
} else { |
|
||||||
"idp-github" |
|
||||||
} |
|
||||||
} |
|
||||||
IdentityProviderBrand::GitLab => "idp-gitlab", |
|
||||||
IdentityProviderBrand::Google => "idp-google", |
|
||||||
IdentityProviderBrand::Twitter => { |
|
||||||
if is_dark { |
|
||||||
"idp-x-dark" |
|
||||||
} else { |
|
||||||
"idp-x-light" |
|
||||||
} |
|
||||||
} |
|
||||||
// We do not construct this for other brands.
|
|
||||||
_ => unreachable!(), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// Update the icon of this button for the current state.
|
|
||||||
fn update_icon(&self) { |
|
||||||
self.obj().set_icon_name(self.brand_icon()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
glib::wrapper! { |
|
||||||
/// A button to represent an SSO identity provider.
|
|
||||||
pub struct SsoIdpButton(ObjectSubclass<imp::SsoIdpButton>) |
|
||||||
@extends gtk::Widget, gtk::Button, |
|
||||||
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Actionable; |
|
||||||
} |
|
||||||
|
|
||||||
impl SsoIdpButton { |
|
||||||
/// The supported SSO identity provider brands of `SsoIdpButton`.
|
|
||||||
const SUPPORTED_IDP_BRANDS: &[IdentityProviderBrand] = &[ |
|
||||||
IdentityProviderBrand::Apple, |
|
||||||
IdentityProviderBrand::Facebook, |
|
||||||
IdentityProviderBrand::GitHub, |
|
||||||
IdentityProviderBrand::GitLab, |
|
||||||
IdentityProviderBrand::Google, |
|
||||||
IdentityProviderBrand::Twitter, |
|
||||||
]; |
|
||||||
|
|
||||||
/// Create a new `SsoIdpButton` with the given identity provider.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the identity provider's brand is not supported.
|
|
||||||
pub fn new(identity_provider: IdentityProvider) -> Option<Self> { |
|
||||||
// If this is not a supported brand, return `None`.
|
|
||||||
let brand = identity_provider.brand.as_ref()?; |
|
||||||
if !Self::SUPPORTED_IDP_BRANDS.contains(brand) { |
|
||||||
return None; |
|
||||||
} |
|
||||||
|
|
||||||
let obj = glib::Object::new::<Self>(); |
|
||||||
obj.imp().set_identity_provider(identity_provider); |
|
||||||
|
|
||||||
Some(obj) |
|
||||||
} |
|
||||||
} |
|
||||||