<commit_before>'''
For managing API calls out to external resources such as sailpoint.
'''
import urllib2
import json
from django.conf import settings

import logging
logger = logging.getLogger(__name__)


# This function authenticates sailPoint and makes a call to the REST api pointed to by the link
# param. If there is postData, it is JSON encoded and posted to the link. This function returns
# a file-like object from which the response can be read.
def callSailpoint(link, postData=None):
    password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
    password_manager.add_password(None, settings.SAILPOINT_SERVER_URL, settings.SAILPOINT_USER, settings.SAILPOINT_PASSWORD)
    auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
    opener = urllib2.build_opener(auth_handler)
    
    if postData:
        jsonData = json.dumps(postData)
        req = urllib2.Request('http://' + settings.SAILPOINT_SERVER_URL + '/iiq/rest/custom/' + link, jsonData, {'Content-Type': 'application/json'})
        return opener.open(req)
    
    return opener.open('http://' + settings.SAILPOINT_SERVER_URL + '/iiq/rest/custom/' + link)

# This function turns a 9num into the user's identity information like name.
def identifyAccountPickup(id_number, birthdate, password):
    
    # Stubbed return
    stub = {
        "PSU_UUID" : id_number,
        "DISPLAY_NAME" : "<NAME>",
        "SPRIDEN_ID" : id_number,
    }
    return (True, stub)
    
    apiLink = 'getPSUPersonByKeyValue/psuid/' + id_number
    try:
        idData = json.load(callSailpoint(apiLink))
    except urllib2.HTTPError as e:
        logger.critical("An error {} occurred for user {}: {}".format(e.code, id_number, e.reason))
        return None
    except urllib2.URLError as e:
        logger.critical("There was an error contacting sailpoint for user {}: {}".format(id_number, e.reason))
        return None
    else:
        if 'Error' in idData:
            logger.critical("The following error was returned from sailpoint for 9num {} - {}".format(id_number, idData['Error']))
            return None
    
    return idData

# Identify a user with an expired password.
def identifyExpiredPassword(username, password):
    # TODO: Stubbed return
    stub = {
        "PSU_UUID" : 'abc123',
        "DISPLAY_NAME" : "<NAME>",
        "SPRIDEN_ID" : '123456789',
        "ODIN_NAME" : 'jsmith5',
        "EMAIL_ADDRESS" : "<EMAIL>",
    }
    return (True, stub)

# This function turns a UDC_ID into the user's identity information like name.
def identity_from_cas(udc_id):
    # TODO: Stubbed return
    stub = {
        "PSU_UUID" : udc_id,
        "DISPLAY_NAME" : "<NAME>",
        "SPRIDEN_ID" : '123456789',
        "ODIN_NAME" : 'jsmith5',
        "EMAIL_ADDRESS" : "<EMAIL>",
    }
    return stub

# This function turns a PSU_UUID into the user's identity information like name.
def identity_from_psu_uuid(psu_uuid):
    # TODO: Stubbed return
    stub = {
        "PSU_UUID" : psu_uuid,
        "DISPLAY_NAME" : "<NAME>",
        "SPRIDEN_ID" : psu_uuid,
        "ODIN_NAME" : 'jsmith5',
        "EMAIL_ADDRESS" : "<EMAIL>",
    }
    return stub
    
# This function returns the appropriate password constraints based on an identity.
def passwordConstraintsFromIdentity(identity):
    # TODO: Stubbed return
    return {
        'letter_count': 1,
        'number_count': 1,
        'special_count': 1,
        'minimum_count': 8,
        'maximum_count': 30,
    }
    return

# This function returns a list of potential odin names to choose from based on a 9-number.
def OdinNamesFromIdentity(identity):
    # TODO: Stubbed.
    return (('jsmith5', 'jsmith5'), ('j.smith', 'j.smith'), ('john.smith4', 'john.smith4'))

# This function returns a list of potential email aliases to choose from based on a 9-number.
def EmailAliasesFromIdentity(identity):
    # TODO: Stubbed
    return (('<EMAIL>', '<EMAIL>'), 
            ('<EMAIL>', '<EMAIL>'), 
            ('<EMAIL>', '<EMAIL>'), 
            ('<EMAIL>', '<EMAIL>'), 
            ('<EMAIL>', '<EMAIL>'))

def launch_provisioning_workflow(identity, odin_name, email_aliases):
    # TODO: Stubbed
    return<commit_msg>Sailpoint expects urlencoded json as a url parameter, not post data. Rewrote callSailpoint to correctly pass post_data. Integrated identifyAccountPickup to make actual sailpoint call given a valid spriden_id.<commit_after>'''
For managing API calls out to external resources such as sailpoint.
'''
import urllib
import urllib2
import json
from django.conf import settings

import logging
logger = logging.getLogger(__name__)


# This function authenticates sailPoint and makes a call to the REST api pointed to by the link
# param. If there is postData, it is JSON encoded and posted to the link. This function returns
# a file-like object from which the response can be read.
def callSailpoint(link, data=None):
    password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
    password_manager.add_password(None, settings.SAILPOINT_SERVER_URL, settings.SAILPOINT_USERNAME, settings.SAILPOINT_PASSWORD)
    auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
    opener = urllib2.build_opener(auth_handler)
    
    url = 'http://' + settings.SAILPOINT_SERVER_URL + '/iiq/rest/custom/runRule/' + link
    if data:
        url += '?json=' + urllib.quote_plus(json.dumps(data))
    
    try:
        logger.debug("Making sailpoint call: {}".format(url))
        response = opener.open(url)
        final_response = json.load(response)
        logger.debug("Sailpoint response: {}".format(final_response))
    except urllib2.HTTPError as e:
        logger.critical("The following error occurred while attempting to contact sailpoint: {} -- {}".format(e.code, e.reason))
        return None
    except urllib2.URLError as e:
        logger.critical("The following error occurred while attempting to contact sailpoint: {} -- {}".format(e.code, e.reason))
        return None
    except ValueError:
        logger.critical("Sailpoint returned invalid JSON: {}".format(response))
        return None

    return final_response or None

# This function turns a 9num into the user's identity information like name.
def identifyAccountPickup(spriden_id, birthdate, password):
    # Dev sailpoint only works for Ty. If it's not him, return a stub.
    if (spriden_id != "959187734"):
        # Stubbed return
        stub = {
            "PSU_UUID" : spriden_id,
            "DISPLAY_NAME" : "<NAME>",
            "SPRIDEN_ID" : spriden_id,
        }
        return (True, stub)
    
    # Build our packet to send to sailpoint.
    data = {
        "user_spriden_id" : spriden_id,
        "user_dob" : birthdate,
        "user_initpass" : password
    }
    
    result = callSailpoint('PSU_UI_ACCOUNT_CLAIM_AUTH', data)
    
    # If any of our tests fail, auth fails. 
    if result["DOB"] != "MATCH" or result["SPRIDEN_ID"] != "MATCH" or result["INITPASS"] != "MATCH":
        logger.info("Account pickup authentication failed with the following response: {}".format(result))
        final_result = (False, None)
    else:
        final_result = (True, {
            "PSU_UUID": result["PSU_UUID"],
            "DISPLAY_NAME": result["IDENTITY_DISPLAY_NAME"],
            "SPRIDEN_ID": spriden_id
        })
    
    return final_result

# Identify a user with an expired password.
def identifyExpiredPassword(username, password):
    # TODO: Stubbed return
    stub = {
        "PSU_UUID" : 'abc123',
        "DISPLAY_NAME" : "<NAME>",
        "SPRIDEN_ID" : '123456789',
        "ODIN_NAME" : 'jsmith5',
        "EMAIL_ADDRESS" : "<EMAIL>",
    }
    return (True, stub)

# This function turns a UDC_ID into the user's identity information like name.
def identity_from_cas(udc_id):
    # TODO: Stubbed return
    stub = {
        "PSU_UUID" : udc_id,
        "DISPLAY_NAME" : "<NAME>",
        "SPRIDEN_ID" : '123456789',
        "ODIN_NAME" : 'jsmith5',
        "EMAIL_ADDRESS" : "<EMAIL>",
    }
    return stub

# This function turns a PSU_UUID into the user's identity information like name.
def identity_from_psu_uuid(psu_uuid):
    # TODO: Stubbed return
    stub = {
        "PSU_UUID" : psu_uuid,
        "DISPLAY_NAME" : "<NAME>",
        "SPRIDEN_ID" : psu_uuid,
        "ODIN_NAME" : 'jsmith5',
        "EMAIL_ADDRESS" : "<EMAIL>",
    }
    return stub
    
# This function returns the appropriate password constraints based on an identity.
def passwordConstraintsFromIdentity(identity):
    # TODO: Stubbed return
    return {
        'letter_count': 1,
        'number_count': 1,
        'special_count': 1,
        'minimum_count': 8,
        'maximum_count': 30,
    }
    return

# This function returns a list of potential odin names to choose from based on a 9-number.
def OdinNamesFromIdentity(identity):
    # TODO: Stubbed.
    return (('jsmith5', 'jsmith5'), ('j.smith', 'j.smith'), ('john.smith4', 'john.smith4'))

# This function returns a list of potential email aliases to choose from based on a 9-number.
def EmailAliasesFromIdentity(identity):
    # TODO: Stubbed
    return (('<EMAIL>', '<EMAIL>'), 
            ('<EMAIL>', '<EMAIL>'), 
            ('<EMAIL>', '<EMAIL>'), 
            ('<EMAIL>', '<EMAIL>'), 
            ('<EMAIL>', '<EMAIL>'))

def launch_provisioning_workflow(identity, odin_name, email_alias):
    # TODO: Stubbed
    return
{'fraction_non_alphanumeric': 0.11334473909324208, 'fraction_numerical': 0.010372112917023097, 'mean_word_length': 3.0419187554019014, 'pattern_counts': {'":': 4, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4856316', 'n_tokens_mistral': 2997, 'n_tokens_neox': 2930, 'n_words': 823}