Skip to content

Commit

Permalink
Merge pull request #437 from Thisara-Welmilla/add-notification-extern…
Browse files Browse the repository at this point in the history
…al-schedular

Add notification-sender-external-schedular sample.
  • Loading branch information
Thisara-Welmilla authored Mar 11, 2024
2 parents 7b8c329 + 474af3d commit 1218bf1
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 0 deletions.
16 changes: 16 additions & 0 deletions etc/notification-sender-ext-schedular/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"email_configurations": {
"sender_email": "<SENDER_EMAIL>",
"sender_password": "<SENDER_PASSWORD>",
"smtp_server": "<SMTP_SERVER>",
"smtp_port": 587
},

"is_server_config": {
"client_id": "<CLIENT_ID>",
"client_secret": "<CLIENT_SECRET>",
"organization": "<ORG_NAME>",
"hostname": "<HOSTNAME>",
"alert_before_in_days": 3
}
}
41 changes: 41 additions & 0 deletions etc/notification-sender-ext-schedular/configuration_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#
# Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
#
# WSO2 LLC. licenses this file to you under the Apache License,
# Version 2.0 (the "License"); you may not use this file except
# in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

import json

# This is the class which read and hold configurations from the configuration file.
class configuration_manager:
def __init__(self, config_file):
try:
with open(config_file, 'r') as file:
self.config_data = json.load(file)
except FileNotFoundError:
raise FileNotFoundError(f"Config file '{config_file}' not found.")
except json.JSONDecodeError:
raise ValueError(f"Error decoding JSON in '{config_file}'.")

# This method is to retrieve the configurations of the email sender.
def get_email_notification_manager_config(self):

return self.config_data["email_configurations"]

# This method is to retrieve the configurations of the WSO2 IS Server.
def get_is_server_config(self):

return self.config_data["is_server_config"]

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#
# Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
#
# WSO2 LLC. licenses this file to you under the Apache License,
# Version 2.0 (the "License"); you may not use this file except
# in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

import smtplib
import logging
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

email_subject = "Your Password Has Expired"
email_body = "Hi there,\n\nYour password has been expired. Please reset your password."

# This is the class which reposibe for sending email notifications.
class email_notification_manager:
def __init__(self, email_config):
self.smtp_server = email_config["smtp_server"]
self.smtp_port = email_config["smtp_port"]
self.sender_email = email_config["sender_email"]
self.sender_password = email_config["sender_password"]

# This method is to send the email to the given email address.
def send_email(self, reciever_email):

message = MIMEMultipart()
message['From'] = self.sender_email
message['To'] = reciever_email
message['Subject'] = email_subject
message.attach(MIMEText(email_body, 'plain'))

# Connect to SMTP server and send email
with smtplib.SMTP(self.smtp_server, self.smtp_port) as server:
server.starttls()
server.login(self.sender_email, self.sender_password)

server.sendmail(self.sender_email, reciever_email, message.as_string())
logging.info("Passowrd expired notification is send to the email address:" + reciever_email)
47 changes: 47 additions & 0 deletions etc/notification-sender-ext-schedular/function_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#
# Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
#
# WSO2 LLC. licenses this file to you under the Apache License,
# Version 2.0 (the "License"); you may not use this file except
# in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

import azure.functions as func
import logging
from configuration_manager import *
from email_notification_manager import *
from is_server_utils import *

app = func.FunctionApp()
config_manager = configuration_manager("config.json")
is_utils = is_server_utils(config_manager.get_is_server_config())
notification_manager = email_notification_manager(config_manager.get_email_notification_manager_config())

@app.schedule(schedule="0 0 */2 * * *", arg_name="myTimer", run_on_startup=True,
use_monitor=False)
def timer_trigger(myTimer: func.TimerRequest) -> None:
if myTimer.past_due:
logging.info('The timer is past due!')

logging.info("***************** Starting the schedular task *****************")
is_utils.get_access_token()
passowrd_expired_users = is_utils.get_password_expired_user_list()

# Send email to each user whose password has been expired.
for user in passowrd_expired_users:
user_id = user["userId"]
reciever_email = is_utils.get_email_address(user_id)
notification_manager.send_email(reciever_email)

logging.info("***************** Completed the schedular task *****************")

88 changes: 88 additions & 0 deletions etc/notification-sender-ext-schedular/is_server_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#
# Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
#
# WSO2 LLC. licenses this file to you under the Apache License,
# Version 2.0 (the "License"); you may not use this file except
# in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

import json
import logging
import requests
import sys
from datetime import datetime, timedelta

# This is the class which contains the utils for the WOS2 IS Server.
class is_server_utils:
def __init__(self, is_server_config):

self.access_token = None

# IS Server related configurations.
self.client_key = is_server_config["client_id"]
self.client_password = is_server_config["client_secret"]
self.organization = is_server_config["organization"]
self.hostname = is_server_config["hostname"]
self.alert_before_in_days = is_server_config["alert_before_in_days"]

# IS server APIs.
self.inactive_user_retrieval_endpoint_url = f"https://{self.hostname}/t/{self.organization}/api/server/v1/password-expired-users"
self.user_scim2_endpoint = f"https://{self.hostname}/t/{self.organization}/scim2/Users/"
self.token_endpoint = f"https://{self.hostname}/t/{self.organization}/oauth2/token"
self.required_scope_list = "SYSTEM"

# This method is to retrieve the access token with client credentials.
def get_access_token(self):

data = {
"grant_type": "client_credentials",
"scope": self.required_scope_list
}
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
response = requests.post(self.token_endpoint, data=data, headers=headers, auth=(self.client_key, self.client_password))

if response.status_code != 200:
logging.error("Error occured while obtaining an access token. Response: " + json.dumps(response.json()))
sys.exit()

self.access_token = response.json()["access_token"]

# This method is to retrieve the list of user Ids whose password has been expired.
def get_password_expired_user_list(self):

expired_after = datetime.now() + timedelta(days=self.alert_before_in_days)
exclude_after = expired_after + timedelta(days=1)

params = {'expiredAfter': expired_after.strftime("%Y-%m-%d"), 'excludeAfter': exclude_after.strftime("%Y-%m-%d")}
headers = {'Authorization': f'Bearer {self.access_token}'}
response = requests.get(self.inactive_user_retrieval_endpoint_url, params=params, headers=headers)

if response.status_code != 200:
logging.error("Error occured while obtaining an password expired users. Response: " + json.dumps(response.json()))
sys.exit()

return response.json()

# This method is to retrieve the email address for the given user Id.
def get_email_address(self, user_id):

headers = {'Authorization': f'Bearer {self.access_token}'}
response = requests.get(self.user_scim2_endpoint + user_id, headers=headers)

if response.status_code != 200:
logging.error("Error occured while obtaining an password expired users. Response: " + json.dumps(response.json()))
sys.exit()

return response.json()["emails"][0]
7 changes: 7 additions & 0 deletions etc/notification-sender-ext-schedular/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# DO NOT include azure-functions-worker in this file
# The Python Worker is managed by Azure Functions platform
# Manually managing azure-functions-worker may cause unexpected issues

azure-functions
secure-smtplib
emails

0 comments on commit 1218bf1

Please sign in to comment.