Giter Club home page Giter Club logo

proffapt / iitkgp-erp-login-pypi Goto Github PK

View Code? Open in Web Editor NEW
26.0 8.0 7.0 127 KB

The only python package you will ever need to implement login process in ERP for IIT-KGP

Home Page: https://pypi.org/project/iitkgp-erp-login/

License: GNU Affero General Public License v3.0

Python 92.57% Shell 7.43%
iit-kgp-erp iitkgp-erp python-package erp-login-py-package erp-python-package iit-kgp-erp-login iit-kgp-erp-login-python iitkgp-erp-login iitkgp-erp-login-python iitkgperp iitkgperplogin erp-login-python iitkgperploginpython proffapt-iitkgp proffapt-erp-login proffapt-iitkgp-erp-login python erp-autologin iitkgp-erp-autologin

iitkgp-erp-login-pypi's Introduction

ERP Login Module

Tired of the tedious ERP login process? Wanted to automate some ERP workflow but stuck at login?
๐Ÿš€ Introducing iitkgp-erp-login: Your Ultimate ERP Login Automation Module for IIT-KGP ! ๐Ÿš€

Key Features:

  • Seamless Credentials & OTP Handling
  • Effortless Session & ssoToken Management
  • Smart Token Storage for Efficiency
  • Supports both CLI & WebApps

Note

This package is not officially affiliated with IIT Kharagpur.

erp_auto_login_demo.mp4

Guess the number of lines of python code it will take you to achieve this.

Reading this doc will give you the answer.

Table of Contents

Endpoints

The endpoints.py file includes all the necessary endpoints for the ERP login workflow.

About

  • HOMEPAGE_URL: The URL of the ERP homepage/loginpage.
  • SECRET_QUESTION_URL: The URL for retrieving the secret question for authentication.
  • OTP_URL: The URL for requesting the OTP (One-Time Password) for authentication.
  • LOGIN_URL: The URL for ERP login.
  • WELCOMEPAGE_URL: The URL of the welcome page, which is accessible only when the user is NOT logged in, and behaves exactly like the HOMEPAGE_URL. However, when the user is logged in, it returns a 404 error.

Usage

from iitkgp_erp_login.endpoints import *

print(HOMEPAGE_URL)
# Output: https://erp.iitkgp.ac.in/IIT_ERP3/

print(LOGIN_URL)
# Output: https://erp.iitkgp.ac.in/SSOAdministration/auth.htm

Login

ERP login workflow is implemented in login(headers, session, ERPCREDS=None, OTP_CHECK_INTERVAL=None, LOGGING=False, SESSION_STORAGE_FILE=None) function in erp.py.

Note

This function currently compiles the login workflow "ONLY for the CLI", not for web apps.

Input

The function requires following compulsory arguments:

  1. headers: Headers for the post requests.

    headers = {
       'timeout': '20',
       'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/51.0.2704.79 Chrome/51.0.2704.79 Safari/537.36',
    }
  2. session: A requests.Session() object, to persist the session parameters throughout the workflow.

    import requests
    
    session = requests.Session()

The function can also be provided with these optional arguments:

  1. ERPCREDS: ERP Login Credentials python file, which is imported into main python file.

    Default Value None
    NOT Specified The user is prompted to enter their credentials manually
    Specified (ERPCREDS=erpcreds) The credentials are retrieved from the erpcreds.py file

    [!Note] Here, credentials refer to the roll number, password, and security question.

    Prerequisites - ERP credentials file
    • This file MUST be present in the same directory as the script where iitkgp_erp_login module is being imported.
    • Create a .py file with your ERP credentials stored in it. Please follow the instructions below to create this file:
      • You can choose any valid name for the file, adhering to Python's naming conventions.

      • Do not change the variable names. Copy the format provided below & update the values inside the double quotes (").

        # ERP Credentials
        ROLL_NUMBER = "XXYYXXXXX"
        PASSWORD = "**********"
        SECURITY_QUESTIONS_ANSWERS = {
            "Q1" : "A1",
            "Q2" : "A2",
            "Q3" : "A3",
        }
  1. OTP_CHECK_INTERVAL: The interval (in seconds) after which the API continuously checks for new OTP emails.

    Default Value None
    NOT Specified The user will be prompted to manually enter the received OTP
    Specified (OTP_CHECKINTERVAL=2) The OTP will be automatically fetched and checked every 2 seconds
    Prerequisites - Token for GMail enabled googleapi

    The token file MUST be present in the same directory as the script where iitkgp_erp_login module is being imported.

    1. Follow the steps in the Gmail API - Python Quickstart guide to obtain credentials.json file.

      [!Note] The credentials.json file is permanent unless you manually delete its reference in your Google Cloud Console.

    2. To generate the token.json file, follow the steps below:

      • Import this module

        pip install iitkgp-erp-login
      • Execute following command:

        python3 -c "from iitkgp_erp_login.utils import generate_token; generate_token()"
      • A browser window will open, prompting you to select the Google account associated with receiving OTP for login.

      • Grant permission to the selected email address to utilize the newly enabled Gmail API.

        • Click on Continue instead of Back To Safety
        • Then, press Continue again
      • The token.json file will be generated in the same folder as the credentials.json file

      Warning The token.json file has an expiration time, so it's important to periodically check and refresh it in your projects to ensure uninterrupted access.

  2. LOGGING: Toggles comprehensive logging.

    Default value False
    NOT Specified No Logging
    Specified (LOGGING=True) Logs every step in an exhaustive manner
  3. SESSION_STORAGE_FILE: A file where sessionToken and ssoToken - collectively referred to as "session tokens" here - are stored for direct usage.

    Default value None
    NOT Specified The session tokens will not be stored in a file
    Specified (SESSION_STORAGE_FILE=".session") The session tokens will be stored in .session file for later direct usage

    [!Note] The approximate expiry time for ssoToken is ~30 minutes and that of session object is ~2.5 hours

Output

  1. The function returns the following, in the order of occurrence as here (return sessionToken, ssoToken):
    1. sessionToken
    2. ssoToken
  2. It also modifies the session object, which now includes parameters for the logged-in session. This session object can be utilized for further navigation within the ERP system.
  3. ROLL_NUMBER is made available for further usage in the following manner.
     import iitkgp_erp_login.erp as erp
     sessionToken, ssoToken = erp.login(headers, session)
    
     print('Roll Number =', erp.ROLL_NUMBER)

Usage

It is recommended to use the login function in the following manner (optional arguments are your choice):

# Importing the erp.py file
import iitkgp_erp_login.erp as erp

# Using the login function inside erp.py
sessionToken, ssoToken = erp.login(headers, session, ERPCREDS=erpcreds, OTP_CHECK_INTERVAL=2, LOGGING=True, SESSION_STORAGE_FILE=".session")

Here are some examples combining all the aspects we have discussed so far about the login function:

import requests
import erpcreds
import iitkgp_erp_login.erp as erp

headers = {
   'timeout': '20',
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/51.0.2704.79 Chrome/51.0.2704.79 Safari/537.36',
}
session = requests.Session()

sessionToken, ssoToken = erp.login(headers, session)
# Credentials: Manual | OTP: Manual | Logging: No | TokenStorage: No

sessionToken, ssoToken = erp.login(headers, session, ERPCREDS=erpcreds)
# Credentials: Automatic - from erpcreds.py | OTP: Manual | Logging: No | TokenStorage: No

sessionToken, ssoToken = erp.login(headers, session, OTP_CHECK_INTERVAL=2)
# Credentials: Manual | OTP: Automatic - checked every 2 seconds | Logging: No | TokenStorage: No

sessionToken, ssoToken = erp.login(headers, session, LOGGING=True)
# Credentials: Manual | OTP: Manual | Logging: Yes | TokenStorage: No

sessionToken, ssoToken = erp.login(headers, session, ERPCREDS=erpcreds, OTP_CHECK_INTERVAL=5)
# Credentials: Automatic - from erpcreds.py | OTP: Automatic - checked every 5 seconds | Logging: No | TokenStorage: No

sessionToken, ssoToken = erp.login(headers, session, ERPCREDS=erpcreds, OTP_CHECK_INTERVAL=2, LOGGING=True)
# Credentials: Automatic - from erpcreds.py | OTP: Automatic - checked every 2 seconds | Logging: Yes | TokenStorage: No

sessionToken, ssoToken = erp.login(headers, session, ERPCREDS=erpcreds, OTP_CHECK_INTERVAL=2, SESSION_STORAGE_FILE='.session')
# Credentials: Automatic - from erpcreds.py | OTP: Automatic - checked every 2 seconds | Logging: No | TokenStorage: in .session file

sessionToken, ssoToken = erp.login(headers, session, ERPCREDS=erpcreds, OTP_CHECK_INTERVAL=2, LOGGING=True, SESSION_STORAGE_FILE='.session')
# Credentials: Automatic - from erpcreds.py | OTP: Automatic - checked every 2 seconds | Logging: Yes | TokenStorage: in .session file

Note

These are just examples of how to use the login function, not satisfying the prerequisites.

Some arguments of login() have their own prerequisites that must be satisfied in order to use them. See "Input" section of login for complete details.

Session status check

The logic for checking the status of the session is implemented in the session_alive(session) function in erp.py. This function determines whether the given session is valid/alive or not.

Input

The function requires following argument:

  • session: requests.Session() object, to persist the session parameters throughout the workflow.

    import requests
    
    session = requests.Session()

Output

The session_alive(session) function returns the status of the session as a boolean value:

Status Return Value
Valid (Alive) True
Not Valid (Dead) False

Usage

It is recommended to use the session_alive function in the following manner:

# Importing the erp.py file
import iitkgp_erp_login.erp as erp

# Using the session_alive function inside erp.py
print(erp.session_alive(session))

Here's an example combining all the aspects we have discussed so far about the login function and session_alive function:

import requests
import time

import erpcreds
# Importing erpcreds.py, which contains ERP credentials

import iitkgp_erp_login.erp as erp

headers = {
    'timeout': '20',
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/51.0.2704.79 Chrome/51.0.2704.79 Safari/537.36',
}
session = requests.Session()

print("Logging into ERP for:", creds.ROLL_NUMBER)

while True:
    if not erp.session_alive(session):
        _, ssoToken = erp.login(headers, session, ERPCREDS=erpcreds, LOGGING=True)
    else:
        print("Session is alive.")
        ssoToken = session.cookies.get('ssoToken')
        sessionToken = session.cookies.get('JSID#/IIT_ERP3')

    # Traverse ERP further using ssoToken

    time.sleep(2)

Note

This is merely a Proof of Concept example; this exact functionality has been integrated into the login function itself from version 2.3.1 onwards.

Using in WebApps

To implement the login workflow for web applications and backend systems, utilize the modularised implementation of this package.

Implementing login workflow for webapps

Following is a proof of concept example to achieve the login workflow:

import logging
import requests
import iitkgp_erp_login.erp as erp
from flask import Flask, request, jsonify
import iitkgp_erp_login.utils as erp_utils

app = Flask(__name__)

headers = {
    'timeout': '20',
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/51.0.2704.79 Chrome/51.0.2704.79 Safari/537.36',
}


class ErpResponse:
    def __init__(self, success: bool, message: str = None, data: dict = None, status_code: int = 200):
        self.success = success
        self.message = message
        self.data = data or {}
        self.status_code = status_code

        if not success:
            logging.error(f" {message}")

    def to_dict(self):
        response = {
            "status": "success" if self.success else "error"
        }
        if self.message:
            response.update({"message": self.message})
        if self.data:
            response.update(self.data)
        return response

    def to_response(self):
        return jsonify(self.to_dict()), self.status_code


@app.route("/secret-question", methods=["POST"])
def get_secret_question():
    try:
        data = request.form
        roll_number = data.get("roll_number")
        if not roll_number:
            return ErpResponse(False, "Roll Number not provided", status_code=400).to_response()

        session = requests.Session()
        secret_question = erp.get_secret_question(
            headers=headers, session=session, roll_number=roll_number, log=True)
        sessionToken = erp_utils.get_cookie(session, 'JSESSIONID')

        return ErpResponse(True, data={
            "SECRET_QUESTION": secret_question,
            "SESSION_TOKEN": sessionToken
        }).to_response()
    except Exception as e:
        return ErpResponse(False, str(e), status_code=500).to_response()


@app.route("/request-otp", methods=["POST"])
def request_otp():
    try:
        sessionToken = request.headers["Session-Token"]
        if not sessionToken:
            return ErpResponse(False, "Session-Token header not found", status_code=400).to_response()

        data = request.form
        roll_number = data.get("roll_number")
        password = data.get("password")
        secret_answer = data.get("secret_answer")
        if not all([roll_number, password, secret_answer]):
            return ErpResponse(False, "Missing roll_number or password or secret answer", status_code=400).to_response()

        login_details = erp.get_login_details(
            ROLL_NUMBER=roll_number,
            PASSWORD=password,
            secret_answer=secret_answer,
            sessionToken=sessionToken
        )

        session = requests.Session()
        erp_utils.set_cookie(session, 'JSESSIONID', sessionToken)
        erp.request_otp(headers=headers, session=session,
                        login_details=login_details, log=True)

        return ErpResponse(True, message="OTP has been sent to your connected email accounts").to_response()
    except Exception as e:
        return ErpResponse(False, str(e), status_code=500).to_response()


@app.route("/login", methods=["POST"])
def login():
    try:
        sessionToken = request.headers["Session-Token"]
        if not sessionToken:
            return ErpResponse(False, "Session-Token header not found", status_code=400).to_response()

        data = request.form
        roll_number = data.get("roll_number")
        password = data.get("password")
        secret_answer = data.get("secret_answer")
        otp = data.get("otp")
        if not all([roll_number, password, secret_answer, otp]):
            return ErpResponse(False, "Missing roll_number or password or secret answer or otp", status_code=400).to_response()

        login_details = erp.get_login_details(
            ROLL_NUMBER=roll_number,
            PASSWORD=password,
            secret_answer=secret_answer,
            sessionToken=sessionToken
        )
        login_details["email_otp"] = otp

        session = requests.Session()
        erp_utils.set_cookie(session, 'JSESSIONID', sessionToken)
        ssoToken = erp.signin(headers=headers, session=session,
                              login_details=login_details, log=True)

        return ErpResponse(True, data={
            "ssoToken": ssoToken
        }).to_response()
    except Exception as e:
        return ErpResponse(False, str(e), status_code=500).to_response()


@app.route("/timetable", methods=["POST"])
def timetable():
    try:
        ssoToken = request.headers["SSO-Token"]
        if not ssoToken:
            return ErpResponse(False, "SSO-Token header not found", status_code=400).to_response()

        ERP_TIMETABLE_URL = "https://erp.iitkgp.ac.in/Acad/student/view_stud_time_table.jsp"
        data = {
            "ssoToken": ssoToken,
            "module_id": '16',
            "menu_id": '40',
        }

        session = requests.Session()
        erp_utils.populate_session_with_login_tokens(session, ssoToken)
        r = session.post(headers=headers, url=ERP_TIMETABLE_URL, data=data)

        return ErpResponse(True, data={
            "status_code": r.status_code,
            "content": r.text
        }).to_response()
    except Exception as e:
        return ErpResponse(False, str(e), status_code=500).to_response()

Example

Now, we will create a script that opens the ERP Homepage on your default browser with a logged-in session.

  1. Install the package.

    pip install iitkgp-erp-login
  2. Make sure that erpcreds.py & token.json files exist in the same directory as the script we are about to create.

  3. Create a file named open_erp.py and include the following code:

    import requests
    import webbrowser
    import erpcreds
    import iitkgp_erp_login.erp as erp
    from iitkgp_erp_login.endpoints import HOMEPAGE_URL
    
    headers = {
        'timeout': '20',
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/51.0.2704.79 Chrome/51.0.2704.79 Safari/537.36',
    }
    session = requests.Session()
    
    _, ssoToken = erp.login(headers, session, ERPCREDS=erpcreds, OTP_CHECK_INTERVAL=2, LOGGING=True, SESSION_STORAGE_FILE=".session")
    
    logged_in_url = f"{HOMEPAGE_URL}?ssoToken={ssoToken}"
    webbrowser.open(logged_in_url)
  4. Run the script.

    python3 open_erp.py

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.