diff --git a/Python/egroup-to-channel.Python/README.md b/Python/egroup-to-channel.Python/README.md new file mode 100644 index 0000000000000000000000000000000000000000..52f3503b0a89a8c75c5c42403a843af61ec384b8 --- /dev/null +++ b/Python/egroup-to-channel.Python/README.md @@ -0,0 +1,22 @@ +# egroup-to-channel Python script + +Tool to migrate egroups to Notification service Channels. + +## Create a Channel for the specified Egroup +- With the corresponding grappa group as channel member +- With email posting permissions for egroup + +Steps: +- Needs https://gitlab.cern.ch/authzsvc/tools/auth-get-sso-cookie/ + - Works on LXPLUS for example +- Edit ```clientapp_name``` and ```audience``` in ```get_api_token.py``` depending on your target (dev, qa, prod) + +Samples: +- Migrate one egroup retrieving information from xldap: ```./egroup_to_channel.py -g it-dep-cda-wf -ldap``` +- Migrate one egroup specifying information: ```./egroup_to_channel.py -g it-dep-cda-wf -a it-dep-cda-wf-admins -o awagner -d "All members of IT-CDA-WF"``` + + +## Disable Egroup posting to Exchange +Run Mail team (sympa) egroup migration script to: +- Stop sending mails to all egroup members via Exchange +- Relay the egroup mails to the Notification Channel email address \ No newline at end of file diff --git a/Python/egroup-to-channel.Python/egroup_from_ldap.py b/Python/egroup-to-channel.Python/egroup_from_ldap.py new file mode 100644 index 0000000000000000000000000000000000000000..2a279f99f9ea394bbe4b36f58871dd61cca29ef8 --- /dev/null +++ b/Python/egroup-to-channel.Python/egroup_from_ldap.py @@ -0,0 +1,30 @@ +import ldap +import json + +def egroup_from_ldap(egroup): + l = ldap.initialize("ldap://xldap.cern.ch") + try: + l.protocol_version = ldap.VERSION3 + l.set_option(ldap.OPT_REFERRALS, 0) + + #bind = l.simple_bind_s("me@example.com", "password") + + base = "OU=Workgroups,DC=cern,DC=ch" + criteria = "(&(objectClass=group)(sAMAccountName=" + egroup + "))" + attributes = ['cn', 'description', 'managedBy', 'extensionAttribute6'] + result = l.search_s(base, ldap.SCOPE_SUBTREE, criteria, attributes) + + results = [entry for dn, entry in result if isinstance(entry, dict)] + #print(results) + + return { + 'name': str(results[0]['cn'][0], 'utf-8'), + 'description': str(results[0]['description'][0], 'utf-8'), + 'adminGroup': str(results[0]['extensionAttribute6'][0], 'utf-8'), + 'owner': str(results[0]['managedBy'][0], 'utf-8').replace(',OU=Users,OU=Organic Units,DC=cern,DC=ch', '').replace('CN=', ''), + } + + # except: + # return None + finally: + l.unbind() \ No newline at end of file diff --git a/Python/egroup-to-channel.Python/egroup_to_channel.py b/Python/egroup-to-channel.Python/egroup_to_channel.py new file mode 100644 index 0000000000000000000000000000000000000000..6e1ba903cb37f77cba0cd53b03cd127c0cc5eecc --- /dev/null +++ b/Python/egroup-to-channel.Python/egroup_to_channel.py @@ -0,0 +1,158 @@ +#!/usr/bin/python + +import requests +import json, os, re +import sys, getopt +from get_api_token import get_api_token +from egroup_from_ldap import egroup_from_ldap + + +BACKEND_URL='https://api-notifications-dev.app.cern.ch' +#BACKEND_URL='https://localhost:8080' +ACCESS_TOKEN=get_api_token() +HEADER={'Authorization': 'Bearer ' + ACCESS_TOKEN} +VERIFY=False # Verify SSL certificate for requests + +# Create new Channel +def create_channel(egroup, admingroup, description): + print('Creating Channel') + data = {'channel': { + 'name': egroup, + 'slug': re.sub('[^0-9a-z-_]', '-', egroup.lower()), + 'description': description + ' - Migrated from Egroups', + 'adminGroup': { 'groupIdentifier': admingroup }, + 'visibility': 'RESTRICTED', + 'submissionByForm': [ 'ADMINISTRATORS' ], + 'submissionByEmail': [ 'EGROUP' ], + 'incomingEgroup': egroup + '@cern.ch', + }} + #print(data) + r = requests.post(BACKEND_URL + '/channels/', json=data, headers=HEADER, verify=VERIFY) + if r.status_code != requests.codes.ok: + print('error creating channel', r.json()) + sys.exit(2) + new_channel = r.json() + #print(new_channel) + + return new_channel['id'] + +# Add egroup as Channel Member +def add_egroup_to_channel(channel_id, egroup): + print('Adding group to Channel members', egroup) + data = { 'group': { 'groupIdentifier': egroup } } + r = requests.put(BACKEND_URL + '/channels/' + channel_id + '/groups', json=data, headers=HEADER, verify=VERIFY) + if r.status_code != requests.codes.ok: + print('error updating channel', r.json()) + sys.exit(2) + updated_channel = r.json() + + return updated_channel['id'] + +# Remove ME from Members +def remove_me_from_channel(channel_id): + print('Removing ME from Channel members') + r = requests.get(BACKEND_URL + '/me', headers=HEADER, verify=VERIFY) + if r.status_code != requests.codes.ok: + print('error removing ME from channel', r.json()) + sys.exit(2) + me = r.json() + if not me['userId']: + print('error retrieving ME', me) + sys.exit(2) + + data = { 'userId': me['userId'] } + r = requests.delete(BACKEND_URL + '/channels/' + channel_id + '/members', json=data, headers=HEADER, verify=VERIFY) + if r.status_code != requests.codes.ok: + print('error removing ME from channel members', r.json()) + sys.exit(2) + updated_channel = r.json() + + return updated_channel['id'] + +# Change Channel owner +def set_channel_owner(channel_id, username): + print('Setting Channel owner to', username) + data = { 'username': username } + r = requests.put(BACKEND_URL + '/channels/' + channel_id + '/owner', json=data, headers=HEADER, verify=VERIFY) + if r.status_code != requests.codes.ok: + print('error setting channel owner', r.json()) + sys.exit(2) + updated_channel = r.json() + + return updated_channel['id'] + +def usage(): + print('egroup-to-channel.py -g <egroupname> [-a <admingroup> -o <owner> -d <description> -ldap]') + print('\t-g <egroupname> is required') + print('\t-ldap will search in ldap for admingroup, owner and description information') + +# Main +def main(argv): + egroup = '' + adminGroup = '' + owner = '' + description = '' + enableldap = False + try: + opts, args = getopt.getopt(argv, "lhg:a:d:o:", ["group=", "admingroup=", "description=", "owner=", "ldap"]) + except getopt.GetoptError: + usage() + sys.exit(2) + for opt, arg in opts: + if opt == '-h': + usage() + sys.exit() + elif opt in ("-g", "--group"): + egroup = arg + elif opt in ("-a", "--admingroup"): + adminGroup = arg + elif opt in ("-o", "--owner"): + owner = arg + elif opt in ("-d", "--description"): + description = arg + elif opt in ("-l", "--ldap"): + enableldap = True + if not egroup: + usage() + sys.exit(2) + + # Remove @cern.ch if any + egroup = egroup.replace('@cern.ch', '') + + if enableldap: + print("Retrieving egroup information from Xldap") + ret = egroup_from_ldap(egroup) + if ret: + owner = ret['owner'] + adminGroup = ret['adminGroup'] + description = ret['description'] + + print('Creating Channel from Egroup') + print('\tName: ', egroup) + print('\tOwner: ', owner) + print('\tAdmin group: ', adminGroup) + print('\tDescription: ', description) + + if not owner: + print("Missing owner username") + usage() + sys.exit(2) + + # Get bearer + # ACCESS_TOKEN=get_api_token() + # HEADER={'Authorization': 'Bearer ' + ACCESS_TOKEN} + + # Create Channel + channel_id = create_channel(egroup, adminGroup, description) + if not channel_id: + print('Error creating channel ', egroup) + sys.exit() + # Add corresponding Grappa group to members + add_egroup_to_channel(channel_id, egroup) + # Remove me from members + remove_me_from_channel(channel_id) + # Change owner to egroup owner + set_channel_owner(channel_id, owner) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/Python/egroup-to-channel.Python/get_api_token.py b/Python/egroup-to-channel.Python/get_api_token.py new file mode 100644 index 0000000000000000000000000000000000000000..5e43cb5cec3628393ddade91c49d5e3cb915df3a --- /dev/null +++ b/Python/egroup-to-channel.Python/get_api_token.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +from auth_get_sso_cookie import cern_sso +import subprocess +import requests + +AUTH_HOSTNAME = "auth.cern.ch" +AUTH_REALM = "cern" + +################# CONFIGURATION ################## + +# The Client application (application portal, option my app cannot keep a secret) +#clientapp_name = "tmp-push-notifications-clientscript" +clientapp_name = "notifications-dev-clientapi" +# clientapp_name = "notifications-qa-clientapi" +# clientapp_name = "notifications-clientapi" + +# Standard localhost uri for this virtual app +clientapp_uri = "https://localhost" + +# The target application (the backend API), with granted permissions to client application for token exchange +#audience = "tmp-push-notifications" +audience = "notifications-dev" +# audience = "notifications-qa" +# audience = "notifications" + +################################################## + +#if __name__ == "__main__": +def get_api_token(): + # Get Token for the clientscript application + # Using https://gitlab.cern.ch/authzsvc/tools/auth-get-sso-cookie/ + # Run with parameters for the clientscript application + # clientapi.py -u https://localhost -c tmp-push-notifications-clientscript + #token = command_line_tools.auth_get_sso_token() + # proc = subprocess.Popen( + # ["auth-get-sso-token", "-u", clientapp_uri, "-c", clientapp_name], + # stdout=subprocess.PIPE, + # stderr=subprocess.STDOUT) + # token = proc.communicate()[0].rstrip() + token = cern_sso.get_sso_token(clientapp_uri, clientapp_name, True, AUTH_HOSTNAME, AUTH_REALM) + #print("TOKEN to exchange retrieved") + #print(token) + + # Do Token Exchange for the Backend API application + # https://auth.docs.cern.ch/user-documentation/oidc/exchange-for-api/ + r = requests.post( + "https://auth.cern.ch/auth/realms/cern/protocol/openid-connect/token", + data={ + "client_id": clientapp_name, + "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", + "subject_token": token, + "requested_token_type": "urn:ietf:params:oauth:token-type:refresh_token", + "audience": audience, + }, + ) + if not r.ok: + print( + "The token response was not successful: {}".format(r.json())) + r.raise_for_status() + + token_response = r.json() + access_token = token_response["access_token"] + #print("access_token retrieved") + #print(access_token) + return access_token + + # Then calls to the backend can be performed with this access token + # ACCESS_TOKEN=$(python get-api-token.py) + # curl -X GET "https://api-notifications-dev.app.cern.ch/channels/" -H "authorization: Bearer $ACCESS_TOKEN" +