Skip to content

Commit 41a00bb

Browse files
committed
Merge branch 'master' of https://github.com/shuffle/python-apps
2 parents d1cbc05 + c63047a commit 41a00bb

File tree

6 files changed

+320
-0
lines changed

6 files changed

+320
-0
lines changed

.github/dependabot.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# To get started with Dependabot version updates, you'll need to specify which
2+
# package ecosystems to update and where the package manifests are located.
3+
# Please see the documentation for all configuration options:
4+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5+
6+
version: 2
7+
updates:
8+
- package-ecosystem: "" # See documentation for possible values
9+
directory: "/" # Location of package manifests
10+
schedule:
11+
interval: "weekly"
12+

gws/1.0.0/Dockerfile

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Base our app image off of the WALKOFF App SDK image
2+
FROM frikky/shuffle:app_sdk as base
3+
4+
# We're going to stage away all of the bloat from the build tools so lets create a builder stage
5+
FROM base as builder
6+
7+
# Install all alpine build tools needed for our pip installs
8+
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev
9+
10+
# Install all of our pip packages in a single directory that we can copy to our base image later
11+
RUN mkdir /install
12+
WORKDIR /install
13+
COPY requirements.txt /requirements.txt
14+
RUN pip install --prefix="/install" -r /requirements.txt
15+
16+
# Switch back to our base image and copy in all of our built packages and source code
17+
FROM base
18+
COPY --from=builder /install /usr/local
19+
COPY src /app
20+
21+
# Install any binary dependencies needed in our final image
22+
# RUN apk --no-cache add --update my_binary_dependency
23+
24+
# Finally, lets run our app!
25+
WORKDIR /app
26+
CMD python app.py --log-level DEBUG

gws/1.0.0/README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## Google Workspace
2+
An app for interacting with Google Workspace or GWS.
3+
## Requirements
4+
1) Enable the Admin SDK API from GCP console.
5+
- Login to Google cloud (Make sure you are using the same administrator acount that you're using for Google Workspace) and In the navigation menu on the left-hand side, click on “APIs & Services” > “Library”.
6+
- In the API Library, use the search bar to find the "Admin SDK". Click on it to open the API page.
7+
- Click the “Enable” button to activate the Admin SDK API for your project.
8+
2) Create a Service account.
9+
- Go to the navigation menu, and select “IAM & Admin” > “Service Accounts”.
10+
- Click on “Create Service Account” at the top of the page.
11+
- Enter a service account name and description, then click “Create”.
12+
- You can skip the permission part here as we will be adding persmissions from GWS console later on.
13+
- In the service account details page, click on “Keys”.
14+
- Click on “Add Key” and select “Create new key”.
15+
- Choose “JSON” as the key type and click “Create”. This will download the JSON key file which contains the “client_id”. Note down this client ID.
16+
17+
3) Subject (Email address associated with the service account)
18+
- Note down the email address associated with the service account you just created it'll be used in the authentication in Shuffle.
19+
4) Adding permissions to the service account from GWS console.
20+
- Signin to the Google Workspace admin console.
21+
- In the Admin console, locate the sidebar and navigate to Security > API controls. This area allows you to manage third-party and internal application access to your Google Workspace data.
22+
- Under the Domain-wide delegation section, click on “Manage Domain Wide Delegation” to view and configure client access.
23+
- If the service account client ID is not listed, you will add it; if it is already listed but you need to update permissions, click on the service account’s client ID. To add a new client ID:
24+
- Click on Add new.
25+
- Enter the Client ID of the service account you noted earlier when creating the service account in GCP.
26+
- In the OAuth Scopes field, enter the scopes required for your service account to function correctly. OAuth Scopes specify the permissions that your application requests.
27+
- Depending on the actions you want to use below are the OAuth scopes required.
28+
29+
| Action | OAuth Scope |
30+
|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
31+
| Reset User Password | `https://www.googleapis.com/auth/admin.directory.user` |
32+
| Suspend User | `https://www.googleapis.com/auth/admin.directory.user` |
33+
| Get User Devices |`https://www.googleapis.com/auth/admin.directory.device.mobile` |
34+
| Reactivate User | `https://www.googleapis.com/auth/admin.directory.user`
35+
36+
## Authentication
37+
1) Upload the Service account JSON file in to the Shuffle files and copy the file id.
38+
2) Now, Inside the GWS app authentication in Shuffle; use the file id you just copied and in subject use the email address asscoitate with your service account.
39+
40+

gws/1.0.0/api.yaml

Lines changed: 106 additions & 0 deletions
Large diffs are not rendered by default.

gws/1.0.0/requirements.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
requests==2.25.1
2+
google-auth==1.28.0
3+
google-auth-oauthlib==0.4.3
4+
google-auth-httplib2==0.0.4
5+
google-api-python-client==2.0.2
6+

gws/1.0.0/src/app.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import socket
2+
import asyncio
3+
import time
4+
import random
5+
import json
6+
import requests
7+
import secrets
8+
import string
9+
10+
from walkoff_app_sdk.app_base import AppBase
11+
12+
from google.oauth2 import service_account
13+
from googleapiclient.discovery import build
14+
15+
16+
class Gws(AppBase):
17+
__version__ = "1.0.0"
18+
app_name = "Google Workspace"
19+
20+
def __init__(self, redis, logger, console_logger=None):
21+
"""
22+
Each app should have this __init__ to set up Redis and logging.
23+
:param redis:
24+
:param logger:
25+
:param console_logger:
26+
"""
27+
super().__init__(redis, logger, console_logger)
28+
29+
def reset_user_password(self, service_account_file_id, subject ,user_email,new_password):
30+
service_account_file = self.get_file(service_account_file_id)
31+
service_account_info = service_account_file['data'].decode()
32+
33+
def generate_secure_password(length=12):
34+
characters = string.ascii_letters + string.digits + string.punctuation
35+
secure_password = ''.join(secrets.choice(characters) for i in range(length))
36+
return secure_password
37+
38+
if new_password == "":
39+
print("Generating new password")
40+
new_password = generate_secure_password()
41+
42+
try:
43+
service_account_info = json.loads(service_account_info)
44+
except Exception as e:
45+
print(f"Error loading service account file: {e}")
46+
return {"success": False, "message": f"Error loading service account file: {e}"}
47+
48+
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user']
49+
50+
creds = service_account.Credentials.from_service_account_info(service_account_info, scopes=SCOPES,subject=subject)
51+
service = build('admin', 'directory_v1', credentials=creds)
52+
53+
try:
54+
result = service.users().update(userKey=user_email, body={'password': new_password}).execute()
55+
return {"success": True, "message": f"Password for {user_email} reset successfully.", "new_password": new_password}
56+
except Exception as e:
57+
return {"success": False, "message": f"Error resetting password: {e}"}
58+
59+
def get_user_devices(self, service_account_file_id, subject ,user_email, customer_id):
60+
service_account_file = self.get_file(service_account_file_id)
61+
service_account_info = service_account_file['data'].decode()
62+
63+
try:
64+
service_account_info = json.loads(service_account_info)
65+
except Exception as e:
66+
print(f"Error loading service account file: {e}")
67+
return {"success": False, "message": f"Error loading service account file: {e}"}
68+
69+
SCOPES = ['https://www.googleapis.com/auth/admin.directory.device.mobile']
70+
71+
creds = service_account.Credentials.from_service_account_info(service_account_info, scopes=SCOPES,subject=subject)
72+
service = build('admin', 'directory_v1', credentials=creds)
73+
74+
query = f'email:{user_email}'
75+
76+
try:
77+
results = service.mobiledevices().list(customerId=customer_id, query=query).execute()
78+
devices = results.get('mobiledevices', [])
79+
except Exception as e:
80+
return {"success": False, "message": f"Error getting devices: {e}"}
81+
82+
return {"success": True, "message": f"Devices for {user_email} retrieved successfully.", "devices": devices}
83+
84+
def suspend_user(self, service_account_file_id, subject ,user_email):
85+
service_account_file = self.get_file(service_account_file_id)
86+
service_account_info = service_account_file['data'].decode()
87+
88+
try:
89+
service_account_info = json.loads(service_account_info)
90+
except Exception as e:
91+
print(f"Error loading service account file: {e}")
92+
return {"success": False, "message": f"Error loading service account file: {e}"}
93+
94+
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user']
95+
96+
creds = service_account.Credentials.from_service_account_info(service_account_info, scopes=SCOPES,subject=subject)
97+
service = build('admin', 'directory_v1', credentials=creds)
98+
99+
try:
100+
result = service.users().update(userKey=user_email,body={'suspended': True}).execute()
101+
except Exception as e:
102+
return {"success": False, "message": f"Error suspending user: {e}"}
103+
104+
return {"success": True, "message": f"{user_email} suspended successfully."}
105+
106+
def reactivate_user(self, service_account_file_id, subject ,user_email):
107+
service_account_file = self.get_file(service_account_file_id)
108+
service_account_info = service_account_file['data'].decode()
109+
110+
try:
111+
service_account_info = json.loads(service_account_info)
112+
except Exception as e:
113+
print(f"Error loading service account file: {e}")
114+
return {"success": False, "message": f"Error loading service account file: {e}"}
115+
116+
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user']
117+
118+
creds = service_account.Credentials.from_service_account_info(service_account_info, scopes=SCOPES,subject=subject)
119+
service = build('admin', 'directory_v1', credentials=creds)
120+
121+
try:
122+
result = service.users().update(userKey=user_email,body={'suspended': False}).execute()
123+
except Exception as e:
124+
return {"success": False, "message": f"Error reactivating user: {e}"}
125+
126+
return {"success": True, "message": f"{user_email} reactivated successfully."}
127+
128+
129+
if __name__ == "__main__":
130+
Gws.run()

0 commit comments

Comments
 (0)