from urllib.parse import urljoin, urlparse, parse_qs, urlencode, urlunparse from django.contrib.auth.models import Permission from django.contrib.auth import get_user_model from social_core.backends.oauth import BaseOAuth2 class TiDBCloudOAuth2(BaseOAuth2): """TiDB Cloud OAuth authentication backend""" name = "tidbcloud" API_URL = "https://oauth.tidbcloud.com/v1/" AUTHORIZATION_URL = "https://tidbcloud.com/oauth/authorize" ACCESS_TOKEN_URL = "https://oauth.tidbcloud.com/v1/token" ACCESS_TOKEN_METHOD = "POST" REFRESH_TOKEN_URL = "https://oauth.tidbcloud.com/v1/token" REFRESH_TOKEN_METHOD = "POST" SCOPE_SEPARATOR = " " REDIRECT_STATE = False STATE_PARAMETER = True SEND_USER_AGENT = True EXTRA_DATA = [ ("expires_in", "expires"), ("refresh_token", "refresh_token"), ] DEFAULT_SCOPE = ["org:member"] def api_url(self) -> str: return self.API_URL def get_user_id(self, details, response): return details["email"] def get_user_details(self, response): """Return user details from TiDB Cloud account""" return { "email": response.get("email"), "first_name": response.get("first_name"), "last_name": response.get("last_name"), } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" url = urljoin(self.api_url(), "userinfo") return self.get_json(url, headers={"Authorization": f"Bearer {access_token}"}) def start(self): auth_url = self.auth_url() url_parsed = list(urlparse(auth_url)) query = dict(parse_qs(url_parsed[4])) query.update( { "utm_source": "tidb_labs", "utm_campaign": "tidblabs_oauth_signup", } ) url_parsed[4] = urlencode(query, doseq=True) auth_url = urlunparse(url_parsed) return self.strategy.redirect(auth_url) # Copy from django.contrib.auth.backends.ModelBackend # Because we do not use django.contrib.auth.backends.ModelBackend, # and social_django's backend does not have `has_perm` method, # so we need to implement it by ourselves. def has_perm(self, user_obj, perm, obj=None): return user_obj.is_active and ( perm in self.get_all_permissions(user_obj, obj=obj) ) def has_module_perms(self, user_obj, app_label): """ Return True if user_obj has any permissions in the given app_label. """ return user_obj.is_active and any( perm[: perm.index(".")] == app_label for perm in self.get_all_permissions(user_obj) ) def get_all_permissions(self, user_obj, obj=None): if not user_obj.is_active or user_obj.is_anonymous or obj is not None: return set() if not hasattr(user_obj, "_perm_cache"): user_obj._perm_cache = self._get_all_permissions(user_obj) return user_obj._perm_cache def _get_all_permissions(self, user_obj, obj=None): return { *self.get_user_permissions(user_obj, obj=obj), *self.get_group_permissions(user_obj, obj=obj), } def _get_user_permissions(self, user_obj): return user_obj.user_permissions.all() def _get_group_permissions(self, user_obj): user_groups_field = get_user_model()._meta.get_field("groups") user_groups_query = "group__%s" % user_groups_field.related_query_name() return Permission.objects.filter(**{user_groups_query: user_obj}) def get_user_permissions(self, user_obj, obj=None): """ Return a set of permission strings the user `user_obj` has from their `user_permissions`. """ return self._get_permissions(user_obj, obj, "user") def get_group_permissions(self, user_obj, obj=None): """ Return a set of permission strings the user `user_obj` has from the groups they belong. """ return self._get_permissions(user_obj, obj, "group") def _get_permissions(self, user_obj, obj, from_name): """ Return the permissions of `user_obj` from `from_name`. `from_name` can be either "group" or "user" to return permissions from `_get_group_permissions` or `_get_user_permissions` respectively. """ if not user_obj.is_active or user_obj.is_anonymous or obj is not None: return set() perm_cache_name = "_%s_perm_cache" % from_name if not hasattr(user_obj, perm_cache_name): if user_obj.is_superuser: perms = Permission.objects.all() else: perms = getattr(self, "_get_%s_permissions" % from_name)(user_obj) perms = perms.values_list("content_type__app_label", "codename").order_by() setattr( user_obj, perm_cache_name, {"%s.%s" % (ct, name) for ct, name in perms} ) return getattr(user_obj, perm_cache_name)