Programmatically create a django group with permissions - django

In the Admin console, I can add a group and add a bunch of permissions that relate to my models, e.g.
api | project | Can add project
api | project | Can change project
api | project | Can delete project
How can I do this programmatically. I can't find any information out there on how to do this.
I have:
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from api.models import Project
new_group, created = Group.objects.get_or_create(name='new_group')
# Code to add permission to group ???
ct = ContentType.objects.get_for_model(Project)
# Now what - Say I want to add 'Can add project' permission to new_group?
UPDATE: Thanks for the answer you provided. I was able to use that to work out what I needed. In my case, I can do the following:
new_group, created = Group.objects.get_or_create(name='new_group')
proj_add_perm = Permission.objects.get(name='Can add project')
new_group.permissions.add(proj_add_perm)

Use below code
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from api.models import Project
new_group, created = Group.objects.get_or_create(name='new_group')
# Code to add permission to group ???
ct = ContentType.objects.get_for_model(Project)
# Now what - Say I want to add 'Can add project' permission to new_group?
permission = Permission.objects.create(codename='can_add_project',
name='Can add project',
content_type=ct)
new_group.permissions.add(permission)

I needed to create a default set of groups and permission (view only) for those groups. I came up with a manage.py command that may be useful to others (create_groups.py). You can add it to your <app>/management/commands dir, and then run via manage.py create_groups:
"""
Create permission groups
Create permissions (read only) to models for a set of groups
"""
import logging
from django.core.management.base import BaseCommand
from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission
GROUPS = ['developers', 'devops', 'qa', 'operators', 'product']
MODELS = ['video', 'article', 'license', 'list', 'page', 'client']
PERMISSIONS = ['view', ] # For now only view permission by default for all, others include add, delete, change
class Command(BaseCommand):
help = 'Creates read only default permission groups for users'
def handle(self, *args, **options):
for group in GROUPS:
new_group, created = Group.objects.get_or_create(name=group)
for model in MODELS:
for permission in PERMISSIONS:
name = 'Can {} {}'.format(permission, model)
print("Creating {}".format(name))
try:
model_add_perm = Permission.objects.get(name=name)
except Permission.DoesNotExist:
logging.warning("Permission not found with name '{}'.".format(name))
continue
new_group.permissions.add(model_add_perm)
print("Created default group and permissions.")
UPDATE: A bit more sophisticated now:
from django.contrib.auth.models import Group
from django.contrib.auth.models import User
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
READ_PERMISSIONS = ['view', ] # For now only view permission by default for all, others include add, delete, change
WRITE_PERMISSIONS = ['add', 'change', 'delete']
EMAIL_USER_DOMAIN = 'your-domain.com'
# Add your groups here, app and model code
GROUP_MODEL = ('auth', 'group')
USER_MODEL = ('auth', 'user')
PERMISSION_MODEL = ('auth', 'permission')
LOG_ENTRY_MODEL = ('admin', 'logentry')
def add_group_permissions(group_names, model_natural_keys, permissions):
"""
Add permissions to the provided groups for the listed models.
Error raised if permission or `ContentType` can't be found.
:param group_names: iterable of group names
:param model_natural_keys: iterable of 2-tuples containing natural keys for ContentType
:param permissions: iterable of str (permission names i.e. add, view)
"""
for group_name in group_names:
group, created = Group.objects.get_or_create(name=group_name)
for model_natural_key in model_natural_keys:
perm_to_add = []
for permission in permissions:
# using the 2nd element of `model_natural_key` which is the
# model name to derive the permission `codename`
permission_codename = f"{permission}_{model_natural_key[1]}"
try:
perm_to_add.append(
Permission.objects.get_by_natural_key(
permission_codename, *model_natural_key
)
)
except Permission.DoesNotExist:
# trying to add a permission that doesn't exist; log and continue
logging.error(
f"permissions.add_group_permissions Permission not found with name {permission_codename!r}."
)
raise
except ContentType.DoesNotExist:
# trying to add a permission that doesn't exist; log and continue
logging.error(
"permissions.add_group_permissions ContentType not found with "
f"natural name {model_natural_key!r}."
)
raise
group.permissions.add(*perm_to_add)
def set_users_group(users, group):
"""
Adds users to specific permission group.
If user or group does not exist, they are created.
Intended for use with special users for api key auth.
:param users: list of str, usernames
:param group: str, group for which users should be added to
:return: list, user objects added to group
"""
users = users or []
user_objs = []
for user_name in users:
try:
user = User.objects.get(username=user_name)
except User.DoesNotExist:
user = User.objects.create_user(username=user_name,
email=f'{user_name}#{EMAIL_USER_DOMAIN}',
password='')
user_objs.append(user)
group, created = Group.objects.get_or_create(name=group)
group.user_set.add(user)
return user_objs
API_READ_GROUP = 'api-read-users'
API_WRITE_GROUP = 'api-write-users'
READ_GROUPS = [API_READ_GROUP, ]
WRITE_GROUPS = [API_WRITE_GROUP, ] # Can be used in same way as read users below
# Adding users to a group
set_users_group(READ_USERS, API_READ_GROUP)
# Setting up the group permissions i.e. read for a group of models
add_group_permissions(READ_GROUPS, [GROUP_MODEL, USER_MODEL, LOG_ENTRY_MODEL], READ_PERMISSIONS)
I also found that using manage.py update_permissions is useful to sort out/clean up stale permissions if models have changed etc.. Its part of django-extensions commands.

Inspired by radtek's answer I created a bit better version (in my opinion).
It allows specifying model as object (instead of string) and specifying all configuration in one dictionary (instead of several lists)
# backend/management/commands/initgroups.py
from django.core.management import BaseCommand
from django.contrib.auth.models import Group, Permission
from backend import models
GROUPS_PERMISSIONS = {
'ConnectionAdmins': {
models.StaticCredentials: ['add', 'change', 'delete', 'view'],
models.NamedCredentials: ['add', 'change', 'delete', 'view'],
models.Folder: ['add', 'change', 'delete', 'view'],
models.AppSettings: ['view'],
},
}
class Command(BaseCommand):
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
help = "Create default groups"
def handle(self, *args, **options):
# Loop groups
for group_name in GROUPS_PERMISSIONS:
# Get or create group
group, created = Group.objects.get_or_create(name=group_name)
# Loop models in group
for model_cls in GROUPS_PERMISSIONS[group_name]:
# Loop permissions in group/model
for perm_index, perm_name in \
enumerate(GROUPS_PERMISSIONS[group_name][model_cls]):
# Generate permission name as Django would generate it
codename = perm_name + "_" + model_cls._meta.model_name
try:
# Find permission object and add to group
perm = Permission.objects.get(codename=codename)
group.permissions.add(perm)
self.stdout.write("Adding "
+ codename
+ " to group "
+ group.__str__())
except Permission.DoesNotExist:
self.stdout.write(codename + " not found")

Taking ideas from the answers of #radtek and #Pavel I created my own version of create_groups.py which I call using python manage.py create_groups. This file is stored on app_name/management/commands/create_groups.py. I created a __init__.py inside each the management and commands folders.
I have created the possibility to control each model permissions separately because I had a group of users called Member that must have different permissions on different models.
I also added the possibility to create users with emails and a default password that would have to be changed afterwards and associate them with a certain group.
from django.core.management import BaseCommand
from django.contrib.auth.models import User, Group , Permission
import logging
GROUPS = {
"Administration": {
#general permissions
"log entry" : ["add","delete","change","view"],
"group" : ["add","delete","change","view"],
"permission" : ["add","delete","change","view"],
"user" : ["add","delete","change","view"],
"content type" : ["add","delete","change","view"],
"session" : ["add","delete","change","view"],
#django app model specific permissions
"project" : ["add","delete","change","view"],
"order" : ["add","delete","change","view"],
"staff time sheet" : ["add","delete","change","view"],
"staff" : ["add","delete","change","view"],
"client" : ["add","delete","change","view"],
},
"Member": {
#django app model specific permissions
"project" : ["view"],
"order" : ["view"],
"staff time sheet" : ["add","delete","change","view"],
},
}
USERS = {
"my_member_user" : ["Member","member#domain.cu","1234*"],
"my_admin_user" : ["Administration","admin#domain.ca","1234"],
"Admin" : ["Administration","superuser#domain.cu","1234"],
}
class Command(BaseCommand):
help = "Creates read only default permission groups for users"
def handle(self, *args, **options):
for group_name in GROUPS:
new_group, created = Group.objects.get_or_create(name=group_name)
# Loop models in group
for app_model in GROUPS[group_name]:
# Loop permissions in group/model
for permission_name in GROUPS[group_name][app_model]:
# Generate permission name as Django would generate it
name = "Can {} {}".format(permission_name, app_model)
print("Creating {}".format(name))
try:
model_add_perm = Permission.objects.get(name=name)
except Permission.DoesNotExist:
logging.warning("Permission not found with name '{}'.".format(name))
continue
new_group.permissions.add(model_add_perm)
for user_name in USERS:
new_user = None
if user_name == "Admin":
new_user, created = User.objects.get_or_create(username=user_name,is_staff = True,is_superuser = True, email = USERS[user_name][1])
else:
new_user, created = User.objects.get_or_create(username=user_name,is_staff = True, email = USERS[user_name][1])
new_user.set_password(USERS[user_name][2])
new_user.save()
if USERS[user_name][0] == str(new_group):
new_group.user_set.add(new_user)
print("Adding {} to {}".format(user_name,new_group))

Related

GraphQL - JWT -change payload of verifyToken mutation - django

I wanna change the payload of verifyToken mutation. So instead of returning email I wanna return id or the whole object
schema.py
import graphql_jwt
class Query(AccountsQuery, ObjectType):
# This class will inherit from multiple Queries
# as we begin to add more apps to our project
pass
class Mutation( AccountsMutation, ObjectType):
# This class will inherit from multiple Mutations
# as we begin to add more apps to our project
token_auth = graphql_jwt.ObtainJSONWebToken.Field()
verify_token = graphql_jwt.Verify.Field()
refresh_token = graphql_jwt.Refresh.Field()
pass
schema = Schema(query=Query, mutation=Mutation)
I've seen this: https://django-graphql-jwt.domake.io/en/latest/_modules/graphql_jwt/utils.html#get_user_by_natural_key
But I dont know how I can implement that - if it's a correct approach
Any ideas how I could do that?
To modify the payload of your token, you have to create a custom jwt_payload function and you must add the path of the function to the JWT_PAYLOAD_HANDLER in your settings.GRAPHQL_JWT
projectname.settings.py
GRAPHQL_JWT = {
...
'JWT_PAYLOAD_HANDLER': 'polls.schema.jwt_payload',
...
}
polls.schema.py
import graphql_jwt
from datetime import datetime
from projectname.settings import GRAPHQL_JWT
def jwt_payload(user, context=None):
username = user.get_username()
payload = {
user.USERNAME_FIELD: username,
'user_id': user.id,
'email': user.email,
'phone': user.phone,
'exp': datetime.utcnow() + GRAPHQL_JWT['JWT_EXPIRATION_DELTA'],
...
}
original jwt payload function
Note that in your case the user.USERNAME_FIELD is your email field, it's the default field in your payload (it's also the field you need to get the token), you can remove it or you can edit it by following the django documentation, if you declare a custom USERNAME_FIELD you can choose from all columns in the 'auth_user' table, but you have to declare it bevor you run python manage.py migrate for the first time

Remove old permissions in django

In my Django site there are some permissions entries linked to applications that I've removed. For example I have permissions entries linked to "Dashboard" and "Jet" applications. How can you remove them?
Permissions have foreign keys to content types under the hood, so removing the content types for the models that no longer exist will also remove the permissions for those models.
Fortunately, Django also provides a manage.py command to remove old content types: remove_stale_contenttypes. Running that command will list the content types that no longer exist and the related objects (including permissions) that will be deleted, allowing you to review the changes and approve them.
$ manage.py remove_stale_contenttypes
Some content types in your database are stale and can be deleted.
Any objects that depend on these content types will also be deleted.
The content types and dependent objects that would be deleted are:
- Content type for stale_app.removed_model
- 4 auth.Permission object(s)
This list doesn't include any cascade deletions to data outside of Django's
models (uncommon).
Are you sure you want to delete these content types?
If you're unsure, answer 'no'.
Type 'yes' to continue, or 'no' to cancel:
To start, make an empty migration file:
python manage.py makemigrations --empty yourappname
Change the migration (this is an example, adjust to your needs):
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
def add_permissions(apps, schema_editor):
pass
def remove_permissions(apps, schema_editor):
"""Reverse the above additions of permissions."""
ContentType = apps.get_model('contenttypes.ContentType')
Permission = apps.get_model('auth.Permission')
content_type = ContentType.objects.get(
model='somemodel',
app_label='yourappname',
)
# This cascades to Group
Permission.objects.filter(
content_type=content_type,
codename__in=('add_somemodel', 'change_somemodel', 'delete_somemodel'),
).delete()
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(remove_permissions, add_permissions),
]
I did it this way:
import re
for perm in Permission.objects.all():
if re.match( r".+modelname.+permissionname.+",str(perm)):
print(perm)
perm.delete()
If you have custom or model based (default) permissions you wish to remove you could write a command like this to accomplish this task:
from django.conf import settings
from django.contrib.auth.models import Permission
from django.core.management.base import BaseCommand
import django.apps
class Command(BaseCommand):
help = 'Remove custom permissions that are no longer in models'
def handle(self, *args, **options):
# get the db name needed for removal...
database_name = input('Database Name: ')
default_perm_names = list()
# are real perms in db, may not be accurate
db_custom_perm_names = list()
# will be used to ensure they are correct.
meta_custom_perm_names = list()
default_and_custom_perms = list()
for model in django.apps.apps.get_models():
# add to models found to fix perms from removed models
app_label = model._meta.app_label
lower_model_name = model._meta.model_name
all_model_permissions = Permission.objects.using(database_name).filter(content_type__app_label=app_label, content_type__model=lower_model_name)
default_and_custom_perms.extend([x for x in all_model_permissions])
# get the custom meta permissions, these should be in the meta of the class
# will be a list or tuple or list, [0=codename, 1=name]
meta_permissions = model._meta.permissions
if meta_permissions:
for perm in all_model_permissions:
# will be the model name from the content type, this is how django makes default perms
# we are trying to remove them so now we can figure out which ones are default by provided name
model_name_lower = perm.content_type.name
# default_perms = ['add', 'change', 'view', 'delete', 'undelete']
# append them to the list of default names
default_perm_names.append(f'Can add {model_name_lower}')
default_perm_names.append(f'Can change {model_name_lower}')
default_perm_names.append(f'Can view {model_name_lower}')
default_perm_names.append(f'Can delete {model_name_lower}')
default_perm_names.append(f'Can undelete {model_name_lower}')
# will mean this is a custom perm...so add it
if not perm.name in default_perm_names:
db_custom_perm_names.append(perm.name)
# the perms to ensure are correct...
for model_perm in meta_permissions:
# get the meta perm, will be a list or tuple or list, [0=codename, 1=name]
custom_perm = Permission.objects.using(database_name).get(codename=model_perm[0], name=model_perm[1])
meta_custom_perm_names.append(custom_perm.name)
perms_to_remove = [perm for perm in db_custom_perm_names if perm not in meta_custom_perm_names]
if not perms_to_remove:
print('There are no stale custom permissions to remove.')
# print(perms_to_remove)
# now remove the custom permissions that were removed from the model
for actual_permission_to_remove in Permission.objects.using(database_name).filter(name__in=perms_to_remove):
# print(actual_permission_to_remove)
actual_permission_to_remove.delete(using=database_name)
print(actual_permission_to_remove, '...deleted')
for perm in [x for x in Permission.objects.using(database_name)]:
# loop all perms...if it is not in the model perms it does not exist...
if perm.content_type.model not in [x.content_type.model for x in default_and_custom_perms]:
perm.delete(using=database_name)
print(perm, 'regular permission...deleted')
If you also wish to ensure that the default permissions are added from Django you can add this snippet in the command:
from django.apps import apps
from django.contrib.auth.management import create_permissions
from apps.client.models import ClientInformation
# add all permissions the django way
# get the db name needed from settings.py
database_name = 'default' # or whatever DB you are looking for
print(f'adding all permissions if not there to {database_name}')
for app_config in apps.get_app_configs():
# print(app_config)
app_config.models_module = True
create_permissions(app_config, using=database_name)
app_config.models_module = None
Then call via python manage.py fix_permissions if you name your command file fix_permissions.py
I've reworked #ViaTech's code to use Django's contrib.auth.management._get_all_permissions() functions which makes it more straight forward:
from typing import List, Set, Tuple
import django.apps
# noinspection PyProtectedMember
from django.contrib.auth.management import _get_all_permissions
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand
from django.db import DEFAULT_DB_ALIAS
class Command(BaseCommand):
help = "Remove custom permissions that are no longer defined in models"
def add_arguments(self, parser):
parser.add_argument(
"--database",
default=DEFAULT_DB_ALIAS,
help=f'Specifies the database to use. Default is "{DEFAULT_DB_ALIAS}".',
)
parser.add_argument(
"--dry",
action="store_true",
help="Do a dry run not actually deleting any permissions",
)
def handle(self, *args, **options) -> str:
using = options["database"]
# This will hold the permissions that models have defined,
# i.e. default permissions plus additional custom permissions:
# (content_type.pk, codename)
defined_perms: List[Tuple[int, str]] = []
for model in django.apps.apps.get_models():
ctype = ContentType.objects.db_manager(using).get_for_model(
model, for_concrete_model=False
)
# noinspection PyProtectedMember
for (codename, _) in _get_all_permissions(model._meta):
defined_perms.append((ctype.id, codename))
# All permissions in current database (including stale ones)
all_perms = Permission.objects.using(using).all()
stale_perm_pks: Set[int] = set()
for perm in all_perms:
if (perm.content_type.pk, perm.codename) not in defined_perms:
stale_perm_pks.add(perm.pk)
self.stdout.write(f"Delete permission: {perm}")
# Delete all stale permissions
if options["dry"]:
result = f"DRY RUN: {len(stale_perm_pks)} stale permissions NOT deleted"
else:
if stale_perm_pks:
Permission.objects.filter(pk__in=stale_perm_pks).delete()
result = f"{len(stale_perm_pks)} stale permissions deleted"
return result

Sphinx-apidoc strange output for django app/models.py

I get some missed info in generated documentation of django project, for example first_name and last_name, email are missed (although they are defined in a parent abstract class). How to control what gets added into documentation based on sphinx-apidoc scan? My goal is to auto-generate the docs based on documentation, but it seems that sphinx-apidoc is supposed to be used only one time for initial scaffolding
I tried to use :inherited-members: as shown below but it still didn't produce first_name, last_name, email that exist in AbstractUser class
.. automodule:: apps.users.models
:members:
:inherited-members:
:show-inheritance:
I execute the following command
sphinx-apidoc -f -e -d 2 -M -o docs/code apps '*tests*' '*migrations*'
Output
my apps/users/models.py
from django.contrib.auth.models import AbstractUser
from django.contrib.postgres.fields import HStoreField
from imagekit import models as imagekitmodels
from imagekit.processors import ResizeToFill
from libs import utils
# Solution to avoid unique_together for email
AbstractUser._meta.get_field('email')._unique = True
def upload_user_media_to(instance, filename):
"""Upload media files to this folder"""
return '{}/{}/{}'.format(instance.__class__.__name__.lower(), instance.id,
utils.get_random_filename(filename))
__all__ = ['AppUser']
class AppUser(AbstractUser):
"""Custom user model.
Attributes:
avatar (file): user's avatar, cropeed to fill 300x300 px
notifications (dict): settings for notifications to user
"""
avatar = imagekitmodels.ProcessedImageField(
upload_to=upload_user_media_to,
processors=[ResizeToFill(300, 300)],
format='PNG',
options={'quality': 100},
editable=False,
null=True,
blank=False)
notifications = HStoreField(null=True)
# so authentication happens by email instead of username
# and username becomes sort of nick
USERNAME_FIELD = 'email'
# Make sure to exclude email from required fields if authentication
# is done by email
REQUIRED_FIELDS = ['username']
def __str__(self):
return self.username
class Meta:
verbose_name = 'User'
verbose_name_plural = 'Users'
My sphinx conf.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import django
import sphinx_py3doc_enhanced_theme
sys.path.insert(0, os.path.abspath('../'))
sys.path.insert(0, os.path.abspath('.'))
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE", "config.settings.local")
django.setup()
# Extensions
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.viewcode',
'sphinxcontrib.blockdiag'
]
napoleon_google_docstring = True
napoleon_use_param = True
napoleon_use_ivar = False
napoleon_use_rtype = True
napoleon_include_special_with_doc = False
# RST support
source_suffix = '.rst'
# Name of master doc
master_doc = 'index'
# General information about the project.
project = 'crm'
copyright = '2017, Company'
author = 'Company'
version = '0.1'
release = '0.1'
language = None
exclude_patterns = []
todo_include_todos = False
# Read the docs theme
html_theme = 'sphinx_py3doc_enhanced_theme'
html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()]
html_static_path = []
htmlhelp_basename = 'crmdoc'
latex_elements = {}
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'crm', 'crm Documentation',
[author], 1)
]
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'crm', 'crm Documentation',
author, 'crm', 'One line description of project.',
'Miscellaneous'),
]
html_theme_options = {
'githuburl': 'https://github.com/ionelmc/sphinx-py3doc-enhanced-theme/',
'bodyfont': '"Lucida Grande",Arial,sans-serif',
'headfont': '"Lucida Grande",Arial,sans-serif',
'codefont': '"Deja Vu Sans Mono",consolas,monospace,sans-serif',
'linkcolor': '#0072AA',
'visitedlinkcolor': '#6363bb',
'extrastyling': False,
'sidebarwide': True
}
pygments_style = 'friendly'
html_context = {
'css_files': ['_static/custom.css'],
}
Okay turned out that I had to use :undoc-members: but it created a mess.
This is required since django's AbstractUser class is not properly documented and sphinx has to be forced to display fields only with undoc-members defined. But undoc-members cause a mess, so the solution is just to add documentation in docstr of the child class for attributes/methods that have not been documented in parent class, after that my documentation got these fields displayed
class AppUser(AbstractUser):
"""Custom user model.
Attributes:
avatar (file): user's avatar, cropeed to fill 300x300 px
notifications (dict): settings for notifications to user
first_name (str): first name
last_name (str): last name
"""

Django show object count on admin interface

I have modified my dashboard with the help of dashboard.py file. There in I have added a custom module/widget called 'My Widget' (haven't created myself but rather just appended as a child referring to a specific django app). Code is as follows:
# append an app list module for "Administration"
self.children.append(modules.AppList(
_('Administration'),
models=('django.contrib.*',),
))
self.children.append(modules.AppList(
_('My widget'),
models=('flights.*',),
children=[
]
))
flights is my django app which has three models. Dashboard looks like following:
On this widget I want to show the count of users who did log in today.
The count itself I was able to get (not on admin interface but through a view accessible via url for testing purpose) with following code snippet:
User.objects.filter(last_login__startswith=timezone.now().date()).count()
How can I show such a count on this widget?
The entire dashboard.py:
"""
This file was generated with the customdashboard management command, it
contains the two classes for the main dashboard and app index dashboard.
You can customize these classes as you want.
To activate your index dashboard add the following to your settings.py::
ADMIN_TOOLS_INDEX_DASHBOARD = 'api.dashboard.CustomIndexDashboard'
And to activate the app index dashboard::
ADMIN_TOOLS_APP_INDEX_DASHBOARD = 'api.dashboard.CustomAppIndexDashboard'
"""
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse
from admin_tools.dashboard import modules, Dashboard, AppIndexDashboard
from admin_tools.utils import get_admin_site_name
from admin_tools_stats.modules import DashboardCharts, get_active_graph
class CustomIndexDashboard(Dashboard):
"""
Custom index dashboard for api.
"""
columns = 3
def init_with_context(self, context):
site_name = get_admin_site_name(context)
# append a link list module for "quick links"
self.children.append(modules.LinkList(
_('Quick links'),
layout='inline',
draggable=False,
deletable=False,
collapsible=False,
children=[
[_('Return to site'), '/'],
[_('Change password'),
reverse('%s:password_change' % site_name)],
[_('Log out'), reverse('%s:logout' % site_name)],
]
))
graph_list = get_active_graph()
for i in graph_list:
kwargs = {}
kwargs['require_chart_jscss'] = True
kwargs['graph_key'] = i.graph_key
if context['request'].POST.get('select_box_' + i.graph_key):
kwargs['select_box_' + i.graph_key] = context['request'].POST['select_box_' + i.graph_key]
self.children.append(DashboardCharts(**kwargs))
# append an app list module for "Applications"
self.children.append(modules.AppList(
_('All Applications'),
exclude=('django.contrib.*',),
))
# append an app list module
self.children.append(modules.AppList(
_('Dashboard Stats Settings'),
models=('admin_tools_stats.*', ),
))
# append an app list module for "Administration"
self.children.append(modules.AppList(
_('Administration'),
models=('django.contrib.*',),
))
self.children.append(modules.AppList(
_('My widget'),
models=('flights.*',),
content='test content',
children=[
]
))
# append a recent actions module
self.children.append(modules.RecentActions(_('Recent Actions'), 4))
# append another link list module for "support".
self.children.append(modules.LinkList(
_('Support'),
children=[
{
'title': _('Django documentation'),
'url': 'http://docs.djangoproject.com/',
'external': True,
},
{
'title': _('Django "django-users" mailing list'),
'url': 'http://groups.google.com/group/django-users',
'external': True,
},
{
'title': _('Django irc channel'),
'url': 'irc://irc.freenode.net/django',
'external': True,
},
]
))
class CustomAppIndexDashboard(AppIndexDashboard):
"""
Custom app index dashboard for api.
"""
# we disable title because its redundant with the model list module
title = ''
def __init__(self, *args, **kwargs):
AppIndexDashboard.__init__(self, *args, **kwargs)
# append a model list module and a recent actions module
self.children += [
modules.ModelList(self.app_title, self.models),
modules.RecentActions(
_('Recent Actions'),
include_list=self.get_app_content_types(),
limit=5
)
]
def init_with_context(self, context):
"""
Use this method if you need to access the request context.
"""
return super(CustomAppIndexDashboard, self).init_with_context(context)

Google Admin SDK: Get a list of groups that a user belongs to

I see that it is possible to list all members that belong to a specific group, as documented here:
https://developers.google.com/admin-sdk/directory/v1/guides/manage-group-members#get_all_members
But is it possible to get a list of groups that a user belongs to? I was hoping that Users.get would contain this information, or the Members API would have something similar, but I don't see it.
So I've found the solution in the Developer Guide, although it does not exist in the API documentation!!
https://developers.google.com/admin-sdk/directory/v1/guides/manage-groups#get_all_member_groups
The best way to achieve this is by using the google admin sdk api.
groups.list()
groups.list() with details
For example if you want to do it using the python sdk, you can use the following
from __future__ import print_function
import logging
import os.path
import csv
import json
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
# If modifying these scopes, delete the file token.json.
### https://developers.google.com/admin-sdk/directory/v1/guides/authorizing
### https://developers.google.com/admin-sdk/directory/v1/quickstart/python
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user',
'https://www.googleapis.com/auth/admin.directory.group']
"""
########################################################################################################################################################
# Logging level set for the script
# https://realpython.com/python-logging/
########################################################################################################################################################
"""
logging.basicConfig(level=logging.INFO)
class GSuite_management(object):
"""
########################################################################################################################################################
# GSuite_management CLASS
# --> This class will have methods to manage the memebers of organization using Gsuite Admin SDK
########################################################################################################################################################
"""
service = None
def __init__(self):
"""
GSuite_management Constrouctor
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
self.service = build('admin', 'directory_v1', credentials=creds)
def list_all_groups_a_user_is_a_part_of(self, userEmail):
"""
This method will list all the groups a user is a part of and return them as a list
"""
listOfEmailGroups=[]
try:
results = self.service.groups().list(domain="yourdomain.com",userKey=userEmail, maxResults=400).execute()
logging.debug(results)
groups = results.get('groups', [])
if not groups:
print('No groups in the domain.')
else:
for group in groups:
logging.info(u'{0} {1} {2}'.format(group['email'],group['name'], group['directMembersCount']))
listOfEmailGroups.append(group['email'])
except Exception as e:
logging.error("Exiting!!!")
SystemExit(e)
return listOfEmailGroups
We can use any google-admin-sdk library like google-api-nodejs-client to query groups of a particular user
This method is using GET https://admin.googleapis.com/admin/directory/v1/groups?userKey=userkey endpoint linked in jekennedy's answer
/**
* Retrieve all groups for a member
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
* #param {string} userEmail primary email of the user
*/
function listGroupsOfMember(auth, userEmail) {
const admin = google.admin({version: 'directory_v1', auth});
admin.groups.list({
userKey: userEmail,
maxResults: 10,
orderBy: 'email',
}, (err, res) => {
if (err) {
console.log(err)
return console.error('The API returned an error:', err.message);
}
const groups = res.data.groups;
if (groups.length) {
console.log('Groups:');
groups.forEach((group) => {
console.log(group);
});
} else {
console.log('No groups found.');
}
});
}
Example usage:
const {google} = require('googleapis');
const path = require('path');
// acquire an authentication client using a service account
const auth = await google.auth.getClient({
keyFile: path.join(__dirname, '../serviceaccount.json'),
scopes: [
'https://www.googleapis.com/auth/admin.directory.user',
'https://www.googleapis.com/auth/admin.directory.group',
],
});
// impersonate super admin
auth.subject = "admin#foo.me"
listGroupsOfMember(auth, "username#foo.me")