#!/usr/bin/perl
#EDIT THE ABOVE LINE TO POINT TO THE LOCATION OF Perl ON YOUR SERVER

##### DO NOT EDIT ANYTHING BELOW THIS LINE #####
use strict;use utf8;use JSON;use MIME::Base64;my$iv='1.01';my$fname='SAMLtest.pl';my$cwd=$ENV{SCRIPT_FILENAME};if($cwd){$cwd=~s!(?:[^/\\]*)$!!}else{($_=$0)=~s![\\/][^\\/]+$!!;unshift@INC,$_;$cwd=$_};$cwd=~s/\\/\//g;use lib qw(.);my%in=parse();my$act=$in{act};my$nolog=$in{nolog};require CGI::Carp;CGI::Carp->import('fatalsToBrowser','set_message');set_message('For help, please refer to the <a href="https://mid.as/kb" target=_blank>MIDAS Knowledge Base</a>');my$midas;eval{$midas=decode_json(SSI_INCLUDE("$cwd/midas.dat"))};if($@){}my$certprefix=lc(substr($midas->{midasid},9,3).substr($midas->{midasid},-1,1));$certprefix='MIDAS' if !$certprefix;if(!$act){if($in{SAMLResponse}){samlresponse()}else{splash()}}elsif($act eq 'start'){samlsetup()}elsif($act eq 'gen_cert_key'){gen_cert_key()}elsif($act eq 'samlsetup'){samlsetup()}elsif($act eq 'samlsave'){samlsave()}elsif($act eq 'test'){samltest()}sub pagehead {print "Content-type: text/html; charset=UTF-8\n\n";print qq~<!doctype html><html><head> <title>SAML (SSO) Test v$iv</title> <LINK REL="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACZ0lEQVR42mNkgAKLiAo1FhaWvWxsbKv3zasrYkACeX3rxFhZWS2AeEdHhscvZDlGGMMmpiYGqGAxBwfHf6AhGhsnFt4Cief0rFEBGnyCnZ1dGCie3ZzsPA2rAU5JTWVAAzqBBjAAFbas7s6uBYnn969fCeSHgcSBhmyoibEJxGqAR0ZHJ1BhGVARSOHJJS0pFoUTN4oxMzM/AfJZoQbcKQszU8VqgG9u73KgggiQAUDFf4GGSfDw8EQDXTUBKPYTKMYOEgfyebO8tL5jGBBcPPkgUKEdED8HKpQEGhDNxcXVAOSrAtk5nJycU0CuABqgn+KicgnDgMjKmTeBitWAOAuocBoQvwUFHBDfBWpSB7rmBZAtAmQHxdnJrUcxILZ2LjMTE9NnoAJOoAIDoM0noU4G2TgB6O/COXvu7AfKOwBdUxRuLt6PYkBCwwJzUFSBNABpOaBtu4CKNaB818JAwz2LDz+eB9ScCBSfG2AgkIJiQGrr0iqgZCsoAIEuYefn518P5HsBDfgO5Ivl+up+WXXqVS3QNU1AfNpHh8cMxYCsrlUbgJr9gfgDMKUJdqw44QFkdwIVz8rx0ZkKUrPhwocYoGsW//nz5+OvX7+EwszE/sENKJiw4RJQgy4Qv2tKchJmwAK2Xv1qC9R86OfPnwy/f//WibaWvgo3oGzatj6g5kKgs8/UxtqaYjMA6AIRoMbXIAOALkhMclJaADegZs4eJqDz1IBOvlcdbf2LAQdYevTp7R8/fqgADZiZ6amZgZIOiAHz9t2bDXRBChDfKQgwUCXZgOnbr+sDvbEY6IL1JSEm9SAxAMzPxxEuAL/aAAAAAElFTkSuQmCC"> <style> :root{--theme-background-color:#FFF;--theme-border-color:#264666;--theme-box-color-dark:rgba(38,70,102,.5);--theme-button-color-1:rgba(137,176,215,.5);--theme-button-color-2:rgba(38,70,102,.7);--theme-carat-color:#3B99FC;--theme-color-1:#FFF;--theme-color-2:#264666;--theme-color-h2:#264666;--theme-gradient-color-1:#FFF;--theme-gradient-color-2:rgba(38,70,102,1); } ::-webkit-scrollbar{width:16px} ::-webkit-scrollbar-corner{background:rgba(38,70,102,.4);border-radius:20px} ::-webkit-scrollbar-thumb{background:rgba(38,70,102,.5);border-radius:20px;cursor:pointer} html{height:100%} body{margin:0;padding:10px;font-family:'Open Sans',Calibri,Tahoma,sans-serif;font-size:16px;text-align:center;cursor:default;background:var(--theme-background-color);background:radial-gradient(at bottom right,var(--theme-gradient-color-1) 70%,var(--theme-gradient-color-2) 100%);background-attachment:fixed} form{padding:0;margin:0} fieldset{border-radius:10px;border:1px solid #FFF;padding:0 5px 5px} input[type=text],input[type=email],input[type=password]{font-size:1em;border:none;box-shadow:5px 5px 10px 3px var(--theme-box-color-dark);border-radius:20px;padding:6px 10px;caret-color:var(--theme-carat-color);margin:0 5px 5px 5px;width:-webkit-fill-available} select{cursor:pointer;font-size:1em;display:inline-block;padding:0.6em 1.4em 0.5em 0.8em;box-sizing:border-box;margin:0 5px 2px 5px;border:none;box-shadow:5px 5px 10px 3px var(--theme-box-color-dark);border-radius:20px;appearance:none;-moz-appearance:none;-webkit-appearance:none;background-color:#fff;background-image:url(data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%2389b0d7%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E);background-repeat:no-repeat, repeat;background-position:right 0.5em top 50%, 0 0;background-size:0.65em auto, 100%} input[type=checkbox]{background-color:#FFF;cursor:pointer;vertical-align:middle;width:24px;appearance:none;-webkit-appearance:none;-moz-appearance:none;height:24px;box-shadow:5px 5px 10px 3px var(--theme-box-color-dark);margin-right:10px;margin-bottom:5px} input[type=checkbox]:checked:after{margin-left:7px;margin-top:1px;width:6px;height:14px;border:solid white;border-width:0 4px 4px 0;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);content:'';display:inline-block} input[type=radio]{background-color:#FFF;cursor:pointer;vertical-align:middle;width:24px;appearance:none;-webkit-appearance:none;-moz-appearance:none;height:24px;border-radius:12px;box-shadow:5px 5px 10px 3px var(--theme-box-color-dark);margin-right:10px;margin-bottom:5px} input[type=radio]:checked:after{margin-left:5px;margin-top:5px;width:0;height:0;border:solid white;border-width:7px;border-radius:12px;content:'';display:inline-block} input[type=checkbox]:checked,input[type=radio]:checked{background-color:var(--theme-color-2)} h2{color:var(--theme-color-h2);font-weight:400;font-size:1.5em;margin:5px} button{cursor:pointer;color:var(--theme-color-1);font-size:1.2em;margin:5px;padding:10px 15px;background:linear-gradient(to bottom,var(--theme-button-color-1) 0%,var(--theme-button-color-2) 100%);border:1px outset #1C2E40;border-radius:1.5em;box-shadow:5px 5px 10px rgba(0,0,0,.4)} button:hover{border:1px inset #1C2E40;box-shadow:0 0 0 rgba(0,0,0,0)} button[disabled]{opacity:.5}button[disabled]:hover{cursor:not-allowed;border:1px outset #1C2E40;box-shadow:2px 2px 6px rgba(0,0,0,0.6)}footer{color:var(--theme-color2)}.arrow_left{background-image:URL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAQAAAAngNWGAAABjUlEQVR4XnWSv07CUBjFixYSTXQxsgGDJDq6+SZicBAWSXwAFwdIkDhQEhcZXRxcfAAJA6EwEZ0oQwNC0tbEGKPGXkos4PEjoS3XxpylveeXc+/3RwCnVpJVoEJllVaSdxagxwT6sFjPrJt11oOFPp38BdPipA50JCnuGFK8IwGTelpcAHMitLFRjkHgVY6NdWjkOuBUHhszYy3gIN7f2JjKc1A9Aq4ifFZi2cHJAREzEFr7wkv7OEUbd5frXiq5GoFKBkPJzTNzAL6Le4vpUhRD5ViAzDouVgbwUtui8kJZMRvMzytmCmQBhlmZQfuB0S1AeoOKZxikVzyUVmYeEYYDkkb3AOkL7xjBIv3gqbTqgTJT3KtvAGi1nVQoF85uZsP5DffqhleM98rP4jZXTISKyfjbcwYF1yeirz0Q1JS/4btLXMNTzgibtv7fCG192nRnfR6EbuvlqG8pohSgk+utWSFoy0CnyK1ZEbAbhaB/cQ8wgMW6Zs2ssS4sDOiEX1xP7UNU0SVV6YtzfgHPGNrJ915RvwAAAABJRU5ErkJggg==');display:inline-block;width:24px;height:24px;background-size:cover;vertical-align:middle}.arrow_right{background-image:URL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAQAAAAngNWGAAABj0lEQVR4XnWSMUvDYBCGo6YFBV3Ebm0HCzq6+U9UdLBdLPgDXBxSUHFoBBczuji4+AMsHUrSTkWnJkNobSGJICJWTJpi2vp6VJP0M8ot9937cMfd93Jgor7tlKBDd0r1bVaZSO830YbrtGzZlp0WXLSp8hvM8UMZ0EQx4wtiRhOBoZzjJ8ACD2NgSmlwbEjpgQWDVB8cKVSgbH7KR8LXwBopP6C+C1wkfXlzhu1KCogYdzQap2G38wXcoNE9CLuSahCo7qEnpiZ7FNfxAdiFYK0kemqeg+Ko38ARL8QEvhAHV1nGE6GSjzoaFA6WXQJhZ7O4wzMsikfoeAGA/vXGeCEirBCcwwM+4VL08Yr3MXgLLgSrwehFISEsFRLZeGUVBmBfBaNVGq3maZkks8wK3giT2GUi59nncQm1exg5Dzg9O3nwtek/Dp71v7Dmmf99oWeOaoEpjmMwPVNKRUyRogYmqaHNTmJeFdCKjM2KgKecxKLG3UIHrtO0K3bFacJFhyqsccNo7KCMJkWZMkb5Akay2skmcKkDAAAAAElFTkSuQmCC');display:inline-block;width:24px;height:24px;background-size:cover;vertical-align:middle}.box{background:rgba(38,70,102,.5);border:1px solid #264666;border-radius:10px;box-shadow: 1px 1px 8px 1px rgb(134,161,213) inset;padding: 5px 10px 5px 10px;margin-left:auto;margin-right:auto;color:#FFF}.box_red{background:rgba(255,0,0,0.3)}.l{text-align:left}.c{text-align:center}.r{text-align:right}.b{font-weight:bold}.dn{display:none}.cp{cursor:pointer}label{cursor:pointer}a{color:#eabe0e;text-decoration:none}a:hover{text-decoration:underline}fieldset{margin-top:20px}.width100{width:100%;width:-webkit-fill-available;width:-moz-available;width:fill-available}legend{font-size:1.5em;color:var(--theme-color-2);position:relative;top:-10px}hr{color:var(--theme-border-color);background-color:var(--theme-border-color);border:0;margin:5px;height:2px} h3{color:gold;margin:5px;font-weight:400} .logo{width:180px;height:110px;margin-left:auto;margin-right:auto;background-image:URL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWgAAADcCAYAAABdyTsCAAA8s0lEQVR4Xu2dAYRbebv//1xAcfXiLS97ueWll6ve5XLVXV56WWvpUtVSBlOGoYOUaKzgEESlQmMFhyAlBFFTWaJmTQk7ahg6qmvK0K6WYau2Wka9Y0P+z/f8zvnOLznZdjL5ncxJ51k+TmbS7ky7u58++32e5/f7f/qXoiiKoiiKoswq5db6yYvZyjffLZW8bxeL975dLDz+erHwRtgRnoV0hQffLhW9ubx/VgWgKIqSIN9lyte/XixCxj2hD0TQQxQCvgYLXv/r+Xz//wR5PvluqXhJBaAoiuIQqZYviZhfCX3wLVgySBXdF3HbmM/hfRCKGpL+x1y29/VC/pYKQFEUxQHfLpUKqJgDKVsyvhhQ6Yu8DTk8hwiEXTKihqTncv1/XM2gmvZVAIqiKIek3+//i0j5/te2mAeEXA24/AmMqI2kpXq2JJ1rqAAURVEOgci5YcvZiPlTQq6Mfi+SNOKOeUg62//H5SV8fE0FoCiKMgZzXu0sYo0BOdtVMUQcCBvPIXLEFreR9NKQpK9mdufz1TMqAEVRlAMicUQzrJ5jco7lzJ+Usy1pNBCLoaSDqAOi3kacogJQFEU5ACLnRwKr5wnkTK4OSxojeHM5RB2QdVMFcDga3Renio3ul3m/cyVXbd8QPOEmPhYuvPyjf0IFoCiflaALv347JOiLuYnkDCjpixkjaTuPvrhU0BnpA+DVVs+LgO9kKssbmXLrQ6bc7O/T4uulEmj0l4qNPeGJvH9r+5+zL2tFUUEvFHaNoJk/Ty7n/LCkJd8eiDoy2yqA0ZSXN05nK8s1EfD7QMKVVl8+jpGrtOUJlgF+HGQNSfcXCzXwJltuamNWUWaVnN85JYLGWJxdQU8sZ8DX0WRHEHXYUx3edRXAPogupFL+SaTcy0Qirrb7OeB3AvI2tRXAj/HjQLbcoqSlKYvnXRWAoswgIs7ztqDZIJxUzsSSdKbMKloqaFTSb0zDUGfQpRKuBWKmlCMhQ8KrfS+ivk8hoMuP5X0I20i6AknX+wtetT+fK+P1TRWAoswYsvkn521A0KUgK6Z0Hcl5Lu/LE0RRh2kYhmN3qKhvH2cBiIAviZR/MxWzETOlXAfdgAJZ5XPU5zxgJG3iDqmiF/DPIVfey1VaX6kAFGW2VrvviKBxngbjDYMbOdtA4pzqCBqGkkVfzbzPV5dPHUcBSKV8W8TcM2IWKGbK1wia4uXTfs1q2iZfkwocuXRJog7Ph6Al7qhsqAAUZbZmoH8cnuAwOJUzuRpvGIqsc3ePX6Sx/KMVZyCaMLIdFnMtYmU0fH9Q0F6YTUuTMMyiK/25bKmfKdavqAAUZXZWvB/H8mfgWs4EcmfDUGajs6iiP+SrrWNRRXeevjspYn5MOfsdVs2Us5UnkzHkbEvaVNH1KOZAHv1MBaAoMyPowg4EzfyZJCBnr4ZnrGEYZtF3j0PlLGL+RRjKmrvAFvMEciZ4j1U0smg0C69KDyBXbs5IFq0oKui90YJ2L2dgRx0cuzNV9O7K09ef9WKFiPnnj8uZY3OTyTleRZuJDsQcmSKq6XsqAEVJvTBaX3wbaxAS13ImjDqy1tidqaLvfK4CkMz5niVne0KDVbM7OXcBP86biQ7TLMyWwNv0jzcqis5AX5AMmoKehpznAaOOoSx6LvP+cxSHTGgUwszZyLkWkzPE7FbOBDLHREeLMYdU0aiodcNQUdKMxBo3hyY4MLOctJyJXUV/a6poee15n5MARJDnsICSrbAhOBU5Fxsg/Diaiy41TLMwW4KouyoARUkxUjn7Vv7MhZLE5Vz40ypa4Hbh59IUfMUFlOnKmeB9fA0TcwSCRhX9XgWgKOkW9IO4oJOWM+DHo7JonHQ393nkzu393Llm587MnBOVMwljDpzRsVjww2mOgsQezXMqAEVJ7xbhE+bPFLSfrJxJPRT10ESHWVx5+BmscF+RJmwvF8ud5Tk9OZNCbdWe5sC4HWSd4hvXFUW3CHco6Hwk6KnImbCKDrYLvWhxZa+z+frEbEcbrd+CI0H9jr0hiNfTlDPhNAdWv/dz6BSvfiuKCno3FDTkTKYmZ7uKzpYRbdgxx/UZPmPjTtaONrghOH05kyD3jla//SiH/qACUJQUsrK1eyJa8b48IOjpyXnBrqIHmoVBzPFoFgVQkcP2JXf+YEbqOpCyLWcwPTmTNcFMcwyN22Gr8LwKQFFShkjxy1iDkExDzgTv283CaCa6V2mtnZzBhZSH+9UzZQrhHp2cm4Idc0RbhdkSqukfVACKkjJklfvKkKB5dvMU5UwgdNMsNDPRqKIvLha+n7X7A83URtsI2D5Q/+jkTPCeF51w5/lRDv1EBaAoKeNituwF+TMFXaOgpybnYgCraDYLzUw0RP14xqrnR3ZjsGDNPB+lnEuGoZjDj2KOvfTNnSuKbhH6doNwzhL09OQMGpQ0m4WIOUwV3ZNZ3S9mQQDl1vqZTJljdVZjMA1yBvg4ijkwblfj4UnZcuOCCkBR0iXoH+W6K8l9KWiA11OVM1gMXkPisWYhIo/CrCylRNVzfmiszkuBnO0qOu9j3K4ebhUG89B6qayipAmZ3thgvBGJ9yjkTIL3rWahJxFHVsil/oD5ztbuSUxuxLPnVMmZgua4nYk5hEq6fo8VRQVd+jUuaDBtORO8F28WXs3gyqYzKT+t7jar56HJDS81ciaQv8mhixy3623v9lO0GKQoeg7HW6tBSEEfkZzJQhhzXM4MxBy3Ut4cfMPsmXI1Ak6XnNdNFR3m0Bi3Yw5daqTnrkJFUUFzxI7iPUo5E8gdjcpsuPptYo4n6T1zo3M9mnv27Oq5nj45l1tG0By3C9a+K8ihMdVxRwWgKClARHg2GrG7agRNjkzO8SranonurW69O5HS6vmXLKvnWLyRIjkTE3MM5NAlNAwfqgAUJQWIfC9d5ARHTNBHKOdm8GSzEKvoJuZA5HEthYfxn49tDTYYb6RMzmQwhzaCFsqvVACKko4twpsm3oCgfQoYz6OU81IJggYiewg6W+I0hzwfpPBQJFnrZvUcyrmbWjkTK4fmuRzZYk8FoCjpuIvwTiRoCHneEvRRyhksAsYcZcYcwts0CaDS3jwt1XMvPLEOcrUFnVI5E4jeNAqthZV8pfWVCkBRjhjZHrzHBmHeyDYFcrYlDdFzmiNc/cZ9eudSNFp3lyfW2XKup13OGxS0FyyssFGI5ZUbKgBFOfotwp8vZtkgDEWbBjkTE3N4ZprjImKOuSyqaT8NAnjZ75+Q6vm9SNrKntdYPadbzoCNQhw3yhxank0VgKIcvaC3oxVvChrPNMgZjI45UEmnYuMt53cKsewZ1NMvZ8IcmudDo5JOweFUiqKCfjtS0GmQMyhbVbQ1zSHNwp7fXj+ZgtG6V+Fa92D1XE+/nCt8zRyaghZSkPMrigq6Fwrakm5a5AxalqAl5sgEMQcEjUz65hGf+XyF2XPNrp5ByuUMloUo5ggE3WajEDl0vbN5SgWgKEdX/Z3mBEdM0CmRM8DreMwBUT884ubgo1j13JgZORN8XGoGjUJuFM5ngyuwdOVbUY4KEfA3Q4ImaZEzsWOOTBRz5HaPSgBVjtZF1TPjDWF25Mwq2m4UelU0ClFNH+HKt6LoFuH12AQHSY+cM+YJuUPQYcwR5NA4HvPC0VTPnbvD1XOR8casyJlA5KZRaN2wIs8HKgBFOSJEzLcuQ9D5mKBTJGcCgcdijstLhakfMI9roXKV9nteZ2VkOqNy3qSgS/bCipnk+FUFoChHt+bduJxlvGGTLjmzio5ijgrEjJhD1r7zU5eIVM03rOusIFMKesbkTJhDm5u+IWfEHHsqAEU5OkE/tPJniJYyTpeciYk5vKo1zZFDbnp6ys3BZ4JdPQszKuc2niDMoYOjRylo+bh9RBubiqLncDyzVryNcNMo54qBMQeWaLIm5vhuPocNQ29aAhCJfpULFlM6RsBGqBD1TMq5ajCC5tGjDcmfq0EOnSs3jmjlW1FU0G+CCtqzBJ1KOYNlPCH08CD/KmMOEfWjKTYH79vNwWKT8cYMypkMNwqZQ8uzoQJQlKMR9B4nOCjodMqZQOQD0xyeSDq3t/binyemFG+8jUbriqyeZ1rOtqTtRiFiDlTSGyoARZkyIoNTaBCKoDHzDOkaUiznbPAMZ6ILJuYIBY1xu7nkD+XvfhM1B+W1LehZl7Mdc6BRyBxatgp15VtRpo2I9KuRgk6vnEnGmubA0srFhTzO6PhpCncONiBoLx5vzLSciQicOXSpwUZhc3VryivfiqJbhNeia67mCxR0muVMTLNQviePZ3MI+bfTiDf2z91YswU963ImFZG+2Sg0jULk0Plq68DLQJ2n7z60N3/vt9Z3+s21l8KLmUNRGt1tEPu4tvJUCqFufxpbhN7wiJ0htXKOV9FmaSVsFuZQUV9KMN64FGsOgsZnIWdSGdgoxMFJZcQdtw8q6OXN1887W7t9AFEvb7zutzZ2IGw804+irMdprr8MBF1dxihqayqCvnvVEvQiBZ1mOUs2WhVMFc2Y46qJOfBsJBhvPBhsDq4zf/485Pw03ig0CyvyrP14UEFL1fyTSLq/sv3P/uqLP+QJWb/rS2XdbwudkPZMoCi/o9AIxF2X6rlYb8voab0/jTXvn6IVb1TMvEUbpFnOoAKWw5iDN62AN0nIubm+c0qq5w+snhlvgATkTKYoZxLm0D4bhTg86fkYgv4BEQAEAEF3X/aD5+r2H5D2bKEoW7tBQdGSCrraQgO9OTVBPxFBs0FIQadczrnwOTrmyMsIXv3LBFa7a9a5G6yek5CzLfSSgPfKzSnIufO07wuBoFtrzKEXvSpijr0DRxwbOwvI6lBxIOaAoClpIAKYARSFgkYFXV/d6pcbKxL9NaYl6MrOXL7K/Jk3aKdbzjYQO4QczkQX0ShENV1zKecduXNQvtZ7O3suUdBu5YzsF2L0/DbAa3wO70HQScsZWDn0Clbo2Sgs1VfOHkTQK1vvzomgUXHgfw8hAIp5JWA3/SjKFjDxnBQd0hzc7JfqnakKendf0E0KOvVyJlHM0bRjDlTS225vTVm5HU1uFE31DEEnImdIGVLEiFtGwGt8Du+hqk1SzkQ+B9nja+YqELQPQaNpeO2gBYAIuocJjnaQRe8OVCOdgHczgaKgyECxUetshIKeQsQh008nBhqEFPSsyJlRB2RvxxxoFkJsZ1wdKypyfpP3Y9Wz81jD83EGhhHiYr4abvHVRJItqaRXIPGk5Uyq8Y1C5NF3xhD0jggaExwQMivnWZWzooL2IejGynQELRL9coSgZ0jOBDIfjjkg64Kb0brVG3nemGLkTEG7krMAAeesg/LnMkXcCYj8N5C2V+uEMUfSct4S2ChE9T7mJAcF/Yg59NN3kPOMC1rRCloijmll0DK1cQUbhBC0vIacwazJmYLOBKvfvkithCoa/OJotO7VUPYsOJIzq+fV8LLWOuUsQIi3RM73BFTXkD5yaAo5ETkb2CjkJIdXRWX/5OCTHC8aaKpgfrSdsKAVpbG6NR1BJ59Bc4vwe2vFm4KeLTl3gJF02CxkzLHo9WRm8eSEiylzzJ6j6rnF6tnRKB2rZ0iQcl7IVx4Hlejq1ml5r5dnDr1OIScl55qpok2jMNooFEEvjnEmh1QcN0XQbBRCzsygHQlgMVfs/88/vo4xn8mPLYDv5hZH/r2q7fWJxPH1xTn7e8MM7WEkgU02/PyP8r9ff9f/7upCf24pj0UKTNEkLUb0GPB1Y98L/tkkJUz8M7m6mOv//b//t//Xf/+P/okTJwg+xufx74AUCa4z6OkJes7zfzANwmFBg9mQM2HM0bBjDhye9P2E1fOzgeq5FTUH11wsoVjZc6x67knlylFBqa4fCxA5YgdUzpRyAnKmoPF1OMlhRu36O3/0Txxs3fv385zk2ExE0JAR/8O0+dd//VfkhQcWQLG2zJ87BMaqJpLJf/ztPwcEkivXDlshQkBj8W//9m/9by/P8w+ZJMiX6yO/9l/++lfI26mYIdy//88/DvJr578HF+eWELHNoKDz/n07f16ioGdHznngC6GkM2aaI4w5PJzR0Z3gUP4r1uSGXT072hAMq2cjwOHq+eHA9xLk4MsQNBp3ECfF61zOYGVrv1FoT3Lky6iovzqIoH/v90+iguYkBxuFSQqaoKo7kACQj//tv/7+WQraFtXl+aX+MoXpjtHC5K/VpZz5z2lc/u+7q5D0zAn650FBt1hBz46cCWMOTHPM78ccexs7f5w4XPW88syLV8947UDO+9VzfkT17FWXWT2DXZnDFkHvhjk0JjkSlTMwOTS61jy8HxEHOtg3xmgUvkXVAzHEJjkSEzRBtfNJAWQKFchkJgWNOGM+4xHEKf/19/9B5TxSVP/9v/8HSbuMGj4mRsg70T+M//Pv/43fT5yNgQw6kOeSVx5ZZS/lS7Mm6Oq21SCEXPEEruUc/5xbOZP4NIcnlak/d+jquY7qOZCzsBFKFUwkZwq6EFSnrY9Vz0QWVn4UQTOHTkrOJGoUBpfILoeNwgryaH8MQT8RMOQPASQoaP5vNZ4EsvpI5YSMFj9nNgXN7210LowM+C9/+Uv8/yz+71tn0kR8MPr3nzBqmpS//vXfbeEiOvqzP2xQLePXGfvDadYE/fZPBO1KznxtpN4IafL9QNBVMKmcWUWb1e8g5giqaMQdP42/1j1cPW8w3phczoBzz0H1vBCrnkdGLtfign7qXs4kzKHNyjcbhdli/eEYkxz3TQ7NUbtEBY289W//+V8HFiIaTbZM/vHt5dkXdDwWSCJ6QAMyVqkjjx6KdBCtuPhao2KLT1X3dtMSwkbUNlOC7pkJDgqaObQrOS+B4HzpGrJhgAgCJ9BB8qygJ5cz4TSHNAhRRUPSbw9TPRdYPZv1asYbk8kZRFuDnHuey7J67n5kseikCLpXqIcLK5aE3ct52zyjRiEEbVa+Bf/5GJMctyDoZqxRmIig8XGsafXv//G3kc0qmfBBNmvntGimfS6CJpXmKqVm/Z5MKM14NIQKFwJcyBWSaBbyD147V88W/XGlOxuCLjRWv5izRuwoW1dyBpGcUc3mymjYCUW8RgQBSUPikLQbOddWwioaM9ENkV3VCBoxR8G/dLjqOZQzpOpIzgCNPs/cWAIpM94QCX5Du42OOZ6xUdjaoITxdClnwJgjnOTIm0YhqujdMc6FvmIq6KkJGu8h2rA/D2nEBMBqefDnfmaC5qhfTNIQ9wTSilXmGHvDe4i0IE/HFTtkTDnbIPq4vJAxcc/Tz0TQItvzc/mYoN3KGRMVQ+vXGH0zsUMZ4obATdThQM4GxhyQMr+2NOAaB66eax+pnieQMwmr53wVebk0NLMlyBnf4ycrU6me7xZq4cKKETRkCxm7kjOph5KG9EvRLd8iaDQKW93tA11/JU7+YqCC3pqGoDk2Z1dx9kwsxBYbR4MAP09Bs9p1JU3+/tngnGT+4ff1xUSahWiEUv6jZY15doxNorKeXUFLrHGNExyWoB3IOXbrNu4LxBGg385nhdwHkTQ+Z6po+foZE3VMImfiCfGYowBJH+iMaM+qno1cN6yJCzCZnAEXUzhax63Bm58SnmTXFwZzaK5mA3dyXjXgPR+CbnQHJjkKtfaBr7+SUbsPGLUbMcmRlKABckf7PTS02EAaqrBRgeG92RX0+DEHZHdYYSH//ehIIwTpvlnIbBlfj1KOw2gFCzvI4WdQ0IV6YahBCFzIGU/edDKXq0COkHOA5y+fFjEvSPSwJ++ZKrrUsATdAYeWM2Cz0MQcnInOlhtfHrB6ZmPQqp5dyBlwMcVqDqKKfo9DmQ5ycJNEI3uBoJtrqG4tAbuUsy3op8OTHJD192NMcjxvdl+Ehya9m5agIa9YZolVXQhyqLpGdfQ5CxpjaK4EDeHFqliv2oxNUSDndtssJIxSEF0hzvrEsg5+3GzNQcv24N15LyZoZ3POPEQ/W0asATmjin4ViUbeW5D3pEnJLNoI2u+Aw8iZsIoePMgf1XTt09UzDkQaUT2DSeXM5iBH69gclGz3wOdXS/W9gQimFG4UUsDO5EzMe2aSw175xrM5xiTHT7FDkxIXNP9XO1bpcTU4LqrjFHEg0z1szBBvOMblh+o18c1Cu+GL1W42EkcvLs2QoL3aT8yfS7aAJ5czPub5zEG8kYOgIcmmLRp5/66JOWrRRAcFnSfjy5lV9OA0Byrp7T+vnteuiOhZPZet6tmJnClo0xzMlOp2c7BXbqyePviUyeoda6PQFrFLORM2Cnl4v49K+tE411+xUUhB/zMpQY+zSAFZ27O0x6hJyCWe8YhXxvh74/uIgShnjNzbFVhawfc0KqvGEsvMCPqxwPw5QwlPvCGIJwV92RK0CHJgA02yzVMi6A/SSLRijknlvMrXjDm8KOYoIGI584nqmY1BwOp5EjmTNeumbJ/NQcl0fx4nnpJY4xwahZzkEBnXKGNnciamUbgRfu9NNArBqzGvv4ofmpSooAlmo/n+Af6DPQ5jdmimHWbiAVHGp7Jfx5uFzJ0hd8IDp8aMdbikk35B+6/mCxA0RGwkSxlPsr7NCpoNQgpaKlmOullRx12I3Io5JpIzYczB1W80CxErFEYugHCsjtUzRO1EznZUArFmccdfkI1ztI4Nt4Mign5vLaxQxHi6kjMJJzm48m1G7Xg/4VjXX21OV9DxeWeCudpRkvocF1VcrT2zOTcGLpqFkHJsi/AAYsUquC1o/F7MhqClQfgego5V0GRcORNGHPNeKOj5QNBoip2NVYOooj1/DxLFz8HSCkV8SDkjpvA4chesfrNZKHn0q3jTbeW3obE6QPlOKGfA5uDQmc9oEv56mIhK5PwzFlbYKIwk7FbOxO+Yle981UxyLHkVVChnDnX9lT1qBxIWdHwlmVtveO9zFDTyfmSyaJC5qiLRYB1VkSJW+hio1mPNQgfxDjLuj0+utLqMOaw4JtWCppTMBEeNDcJMeVmYWM7xCnqpQEHTLvEq+oGJOZhDU8Tjy5ngPQiRzcL5fAWSxsfXWYk2urcZbQxWz07kTOxzNwpViTc4Wneo41Aly75tLpKloEPpupbzi1DQ1iRHsY4KGgf5z7m+/sq9oFlJUlY8o2O0DGb4sKQ8xt9QNUJMrg9LwijimE0/NhVdNAsh11GRCQ5BQuSBGAQgtvqzDBrN0vQLWmKEs/NmxC48FyOU7aRyJraggwoavP8z2UgMcSEQdLHBmMMS8thyJsF7zKIhaeTReL6VavYbke+CSHMPcuZY3bJVPbuSsx1vIBNH9Zw1o3Uvdq2zlcfLoc8bQZtGYY2CdivnRvcFG4XmTI62meQoVBF33HZ+/ZV7QZNMscqqDtKdaUGPDxY4DitnCBVijW0OHnAczkmzEFNAjCzGB70IRFrpF7RI+dK8R0FTtg6ODOXrIUFzxO4jVfSOyaGbFLQ3iZxtSSPqCEf/EHdkSsHX6Im8e3E5g3Vncq7Y8QbiliAPL0HQqELvTXKRrQh6LxQ0K2iDMzkDvIbgzSQHqody0CiEqO87uv4qeUHHOQ6CRhWJUUMsjrhes0a2P0527aJZiOofm4Kojse5tMAepUy/oCV3voEJDggaDUFKd/LznClpK+LA9iBmoZ99fPW88cNiwRI0BTyBnImRNOIOiBpPHoZEOYNNSNWhnBlvmBG1CjYHGW+gAj1Pgx0uh/7FTHKsI4KggB3KmWDt2+fKtwjajNo9Prrrr3jlletrlvTKq9Hxhv11uHl5QBBBxL7XSTb8EPfge+BMO+FSEhqC+PeBvwczJOj6D3aDkOKdTM5xQTODhqC9jwpapPnVYtFU9LmgUUgJH1LOXTAq8jBiZuW8noycAeOSwWNFIegFNisPj8i5Ya18U8CO5cyYwzeTHGwUCr9BvuNcf9VcczrJoSgUNpqCmBCBSN3fu2gE7ScraK55/yjYI3bAyU0oWTBa0J+8YVsq6PfRPHR01RQZX84AIo4TydaSqns52/HG6n68kQ3iDcjtzqSClsr5mr3yTUEDR3ImEP9Ao7AWNAoRtRz0+qtI0Muc5JiBG74V5Wko6LXpCfpxIGjmz208J5ZzXNAVClqeDz+ZjRcbD/cbhTFBO5Qzb+cmjuVMyvbBSPkKBC3I5mCTm4OHZnVr95QlaCNlI1s3ciYv8TQr3/j1+G1OcsgSxLmxrr/iJMc7I+enJt9LJYqyaZBlK0QySQuaW4Q7doMwS0FPImdiV9BjCVrE/P2StbASyLhOKU8k57ikiXM5E8YbbTvegKifuIqrRNCvrEahJVyXcgamUVhpGUHnSnXJoat4fX3c66+QCWIqAMiWIbK91KIoqJwhZ8QovvQjilMQ9B4FbVXQk8iZTFBBi/BOW5MczIs94ErOhBuCjuVMIPDh1W6hiOYajxV1kEM/sBqFlnQdyZmgUchJDjYKvWrrh3Guv6p3t1CJ8F94TAKg8eILtYBNPFOBovgWkDNy7mKtjSWnZAQt0vnCOoPDrqAnkLOLCpoxx2+ZcGElbwRNZkXOzJ/t1W7GG6W97XfW7PPkObRXjAn6hVM5N81TZB6b5MDI3YMxDk26VRMhg2r4L3up0UGmZ2is8OO0oShFAXIuVCWCdS9oxgjfGEHXIWgIl4IeW87uKmgicn5oGoUtNAop4FmSM+MNrnYHZ4FAzhiz4wW2LpCq9lysghbBOpQzaWAemo3CFhqFiDqej3Fo0hWIWfJ3iUYw7thElhfSDGmkF0UxYiZJLKncWAgPSRJZU7oQsgs5U9C4TSVHQeO4zwMtNYiYb9mC9ijhNMv5wPEGmoVX3P9fUbcXCLptCxq4k7NUv3ifgi6Yw/tRRfN+woNcf4WlD6/aRPUx8yhKEksqP1j5M6XrRs62oOv7gl4IBH33gFdOfTWiggYzIudNYWS8gSYhbxd3iXytnYo51Y4CBg7lbIgahc3BRmFzdesLCFhRlMkF/eNicUjQFeBCzp3BCjo/rqC5VfjBmuSgiGdDzoDTG3a8gUq6kZCgH5VbnIVGBc0q2oWcCXJoe5Kj3Ahy6FJ95ZIKQFHcbBE+jhqElC9wIGcQr6C9sQUtUxxPBic5uiD1ciZmOSWMN6o4ewOCxkFDX7oVABuFDYkdMGGBCpcixtOFnAkbhcHtKqZRWKwhT76lAlAUN0sqOzFB4+lAzvnwSUHjLsBFI+g5CvpAOfRdc6DRMgRNGadezm08IXKznDIQb+TLXIt2jYj5piVoe7nEkZzBjhA1CmUtXrYjvUrQKMTzngpAUdwIem/RbOtBvsSBnHmb9khB58reQYUjkw/XOGrndywhp1vOVSHKn7mcko/ijVojKQGIlL/B16zEBe1MzqQ7YpKj3HisAlCUCRHZfRmewYGROvv8DCdyBjkwQtDzYwha5HFqUNCrFG+q5Qw4XreMzJnxhsQdF5ISgGxMn6SgOxQ0cCbnVghz6GYX0QYahZD0GxWAokw+A30D1XPYIKRgIeWJ5UwYcaBqxiFJ/YtjCZo59I41yZF2ORvkY5E7b79e9Ez+zOmNBJFY5W0k6LolYEdyJniPjcJaNMnh99/1J1u+URRtEJaa9xhvWJLFazdyjgv6cijohXzl2jjCyZZbD/YbhauUbwrlTDheh6NFRVwL+9MbD6Yg6F/2l1UoYZdypqCRa1eXpRFa63CSo9JcPa8CUJTJRux+WeR4nTV1ARzJGTDisAXtVa+MeS2XZ01y8JCjdMqZgma8kTE3pwBU03NJC0Dk/JM9C93gcglwIGewbp4NTHLg18pJDh+yvqECUJTJRuz2BuINX3As59EVdH5sQYt0z0kVPULQIFVyJhBkMbwYdsnEG0J5zz4zOcFJDt8StD0a50zOwOTQnORgo1A2C++qABTlkIhYeWNJNhI0cCnneAVNQS8W/G/GlY4Iei8maJAuOZNyixfDmvG6XBBvdKchAL+9eQO58ICgTVPPlZwJxI4Djypm5Rs5NCrpn1UAijI+PGt5IH+GSIFLOddWKOjBJmEeM8FnxxZ0pfUrZI/7BCHjEgUN0iPn+HhdDfkzBA1Zfz8NAUjscD7IoGOCdinn14KVQ1tnQ0sW/UoFoCiHPyTpvhATtFM5U9DxiCN3CEGLnB8KnIUORYxnmuRMQRfr9vZgGZLudTZ3Tk5DAOLMU4w4VrYhYS6XNB3JGdg5dBWRDgRdDkbt9lQAinJIlorN5/H8GbiRMxkdcUBcZ8aPZdr+wKgdBZ0SORN7vI7xBsbsfpmmAETQu+HVV7aggRM5g+VQ0rgZRXJvNAdxJnSQQ9faG2dVAIoyJnIH3EmJN3rRejfknKeg3cnZGxB0baCCPoxwJNq4nrMEzbsEUyNnzj9zvI5XWwnZUr0wZUE/r7bDbUIKGjiSM9jAa9Mo9Nuc5ICg8fqaCkBRxkSagtcWi6PiDeBMzmB0xLF4OEGLaM/lhitoXPCaEjmT6PQ6sz3I/FmmHM5MUwDyfXQFbhNagnYmZ8BGYeepOXwfkxylGm6buKMCUJQxkcr5vh1v5P0VCNqlnOOCLkgFnYWg84K3e1jpiOx7eb/NChqCTo+cmT/z9DqJNSBoxBxTb5rJ99IQQWNZhYJuUdKTy5ngvbXYyjdEfYiFHEXRBuGOFW9Q0K7lbAt6MRB0CdUzzoR+M4Ggd3BtVKFmKmiItgRSIeennH+2DueHoDHJcX/aApCK9ntrm9BeLnEkZ2JyaDPJETYKcR1Q41cVgKKMQam1dnpwegNipaAdytkWdGtA0FczhWeHn99uP7JmoSFnkBI5gzB/rjF/Bshlp57HyujbBRNxPBV5ooIOZStPJ3ImdqNwnY1CqaJ1kkNRxkFijZsD8UYg0RW8dizn1SFB+yLoYijo4qPDn8DXvpurWrPQRroQ8tHLuSPE82fQW9l8fXLaAug8fXe6IsKshRFHyxK0QzkTu1HoVSDoGirqr1QAinLw/PnhUrnJeIOjcMCdnAkEnS0ji/UhZggalfTDw0pHxHzTEjSli+eRy5nnb6wGp9ctRfmzV92evgDYKNzzO2ZZxRI0cCLnNogEvRY2Chur3CgUWd9UASjKwfPnt0uD8QZH7FzLGR8bQTdx9gaiDTQJMXJ2d4Ibq89D0Hk/JugjlbMv2PlzbiB/rt89MkG3N17tbxNG4nUkZ/I7Pobk7TsKIWjk0Xq7iqIcBK+++uXw9AYFDRzKuVDfF3QGgs6LoJciQVcOPX71Ws4ZjgRdqNuCPlI5A+bPkJOdP3vV1pFdoipyfmSdx2HJ15mcyTJyaB492kaTEL/2JyoARTlY/nzLxBvLYbyxSkG7ljMFDVmVGjj/mYKW194k0pEpjjfWsgrEe5RyJlgKKQ/nz3n79LrpI/HGvfBuwpigncl50zwHc+gOGqOQ9AFHKhVFD0jaCKc3UD1T0DnfvZwp6GpM0JDXlQkF/RjitwQNjkjOxDQIg+NFmT9D1EdaQUomfIuC7oqguf0H3MiZBIJ+aY4eZaOw3m+sPOWCjqIoI0AVJ/HGHgTN6Q1W0C7lTPAeBT0vgr4cCNpDJnt+oqjG79yLZqEp6GVKeqpyJvJexZy/weNFF8x695Fu00m0MRese3eMoCFZCtqRnEmUQ9uNwnIdt6ssqAAU5SOIlC9ZZz9DziAROROrgp7PUdDYsJvoEB35+xaMoFco6AoFPVU5E57/bI4XFUEH+TM+PtKrn0SaZ/G9GUGzgoaIXcqZMYdZWNnCeB1z6FKtU1MBKMpH8+fW3YzZHrTjjUTkTKIKOjrJbskLBD1pJivTGxdGChpMXc5bVoMwut7KR/aMKvp9CgSARmEvPI/DiNjkxW7kTN4JYQ5tGoXMoWXle0MFoCgfz5+fC5h1NsKloBOS82hBg4m3y0Q0X0SCLjYoaDB1OVPQ5vzn8PbuCgSNSvrnlAj6Ny6rUNBu5Qw6eEbz0NbCikiaq/2Kogwh1c1JiTd6GSveoKATkXN3IOJYsgQtjcK3LqQjgt6N1r0hZCPeqcuZgub5G6U682eRdSElgn5s1r23EUFQ0MClnAHew8FJ9h2FaBSubr4+pQJQlNHnV1xnvCEC9ixBJyNnsApigp7LFLedCNpvP/esZRWI1zBdOdeEaP4Z43V2/lxdXjudBgGInH/029wmhKBD3MqZgl5Ho3DwjsJqq/sns+CKovPPDwbjDQo6ETkTjPBB0CKt+Wwo6GzJxdgZVr5/CmehbUFPXc5+KOhSg9dbQdB4Pk+LAGS770607t2EoI10XcuZgl4e1Sisd/5kmkVRNH9+E8QbUfVcp6ATkXOxwYjDOgu6BEGjkn7oQjqF+soP3rCg28L05AzwHr6Ofb2VEIzXpWZyQaKNBWvdm9KFqB3KmbTNwop1sl0DZ0Q/VAEoSnzi4ayJN8x6NOQMKOQE5AzwerSgK01Hv64FCrq5BglDsNOSM/Fj43VBgxCV4/kUXXF2bijigGDxdCvnp6yizcKKtVEo6C3fijKM5M63rHiDgk5QzhQ0BM+jRjNFCBobhU4qSxHwOY/r3ragwRTkvCIw3uiG8YaP6hmn171N2ZLSiUDQZt2bgoaEHcqZsFGIbL7BjcLernwfKgBFGZx//lkQQUfxRpfxRkJyjgvaq1LQ8tpzJR4RdG9f0BtG0CBxOQNOb4yKN5rpEQAbhW/3Bf2agnYr51ij0ExyVFoQNJqmw0s7iqL5c5b5MwWdpJwJBJ0tDwgaVeaCM0HXOjtxQScuZ+IPnF5Xo6CL9c659Al68xezTUhBc/vPoZyBiU8g6BVrkqNcR9xRUAEoSohI54v9/JnxhpFvQnIm9UjQTVvQENkVhxX0RoEH929QyknKmfBwJB7OD0Ej5nieRgFI9fzAWlaBRClph3ImmK/mHYW1dtQo/FEFoCicf+4sxPPnKciZgu5Q0DisH6fZ5as8h2NipAnZDGahzSQHpZugnIndHMwW65AzgKxvpVTQvrWsYgnavZzbwDp6NJzkQBbNGXhFUUFX2rVoe9CrccMPr5OTM6Gg0RikoP32urONMmnOXaegmzJqR/EmKme8x+o5X2H1jBXv969T2ggTWd4wFfS2qaApU6dyJpC+fUdhvjJ8iayi6ILKY47XGcHimaicSX20oB3P956GoKWSZg6dlJxJZ1T2zOo5tcsYkjufD2/3ZgXNatetnDkLzZVv6xLZWnv9SxWAopgG4W6YP1OyzKCTkDNZsypoHtaPHNr5+Jnnt99C0Iw5kpEzMYspnNywqucqq+c0stPvnxyMOCDZXcjUtZw5IcKzoZtSHJgzOSDrGyoA5dgjsjrD21NqKxTsVOTcpKAHblOZyxZ3nAu61rnPo0cbXRyUlJSc8Z51rGgLx4ra1fPttAugJkYOBL02JGjgUs7A3FHIS2SLfhujiHg2VQDKsUdijeuZStQgpGhRQScsZxBm0P6goOdzpWeupSNZ8Jcya93zeD40JI082qWc2RjkjdXhqXWc3OAZ1ylGZPncbBO+hEAp2zZwKGf7bGhk3r5Z+YagMRP9WAWgHHtEzPcG82dW0InKGZTwrA+cBY0MGk+ej+w45riHhZiBShpxh5VJT1g5U85GNGwMgp40wTj3nHJBd4V+k4LepXRdypmCjk9yIObQS2QV/Uvk/MtAvNEQOGaXrJwpaH9A0KikHyQhnrUXf5yUZuRbiBM3mmA2GpIuDzYODylnVN+Us5naGIg2GnfTLwA2Ve9C0JAmBQ3cy9nAw/s3OcmRRaOws3FWBaAc9xXvD7mYoKckZ8CzoGuINhBxoJGWmMwkFz6XLTV20ZTEuRiexBBFE3mgeThK1J+e1uhsogpH5izSR4batHNnTHA8YrQxA4iYv48EjeoWcjY4lzMr6FGH95e0UagcZ0SuX2Xt/JmCTlbOhIJehqBZQVPQCSGTFZekYt9DPmyq6VaQFxfrJvbAYkk1Juo4eA9iRtVcqkMsy8ic7coZon42M3LmqN3OBSPolxT0yhYE7VTO8Vu+V7bMyrcfrHzjeU8FoBznBuFN6/wNyNlQT1zOcUEXTAWdsKBJqbH6VaZU30HlTlHbFXUjPJ60tS5sQMSkAlrreB9ihtwRaZhZZ69iy3l79envJ2dNABs7/S9GCxq4kjPhJIdEK/bZ0Ig6nqkAlOPcIGzaDUIjZzAVOccFnQ0EjQrUm4aIutu7J0XMP6HiBcjBJf6AqPE9QbzIqVFZh6yGrODzqJgpZjYDDZD+fVbOM4gIeg/nccQF7VbOFLR9eH89EDRy6GO9UaiooDes/BlChaSnJud4BT1VQROp2M6LUDcgWSPqGmWdK0c0IWN5AsijPkrM+Pi9RCbXZ10AMmb3CsLEsgpEDEGvMIt2I+e4oF/2fevw/myxhkOUvlIBKMd1gmMHgvYsQeM5LTnHBV3uz8UEPWVRlxsPJGL5IEC2hgLwAYUcQ8SMFW5uCc44Ejc8srYJIWhW0e7kTIygBxuFEDTiDk8FoBw70LgSQfdE0MyfeQXVVOS8Hm8SUtA1CvooePlH/4TEHDelQn6IW08WzO3bIuJhKrvyY36Was+LiXn2Bd2sr26F24S/24J2K2dW0GwU2rd846lHjyrHD5Hpuax9gl2DFfRU5FxuhYJuDEYcOA9apHczTbKqttZOS958SaS9IDL+Xl5fkSrvsz7MR6rZWxC0PC1B/xOCdi5nCto0CsNZ8mDlG1n0ryoA5diRr3Wu2w1CyJUVdPJyBqOahBA0st8rWgAcLXKQ/hzXvWOCdiln8Hpf0N2BSQ7EHMfyjkJFR+x+GGoQArxOWs4knkGroNNCd/uPM/Z5HCtPKWjBqZwtQb8ePhsagkbk8Y0KQDluExwP2SCEZIO4YTpyJgOC9rWCTl8O3eMstCVoh3Imy2B9RKOwVMN25m0VgHLcBP2c+XMkWXlORc4k5RW0Cvo3qWghTo7aQdDu5UxQsfPo0YIfNAoh6ocqAOW4jdh9yIcTHBRtY2pyJmwScsyuiBnYdFy7r4J+bK6+2oGQKehE5ExB7/AS2YJpFAr1HRWAcmyodjZPRxMchbot6GnJmUD2sQq6UGvrKWbpODTpPmTJUTsuqiQg5480CrEQtP7yj5MqAOVYIPI8F05wQNCU7fTkvMHXVgadOkGroF/egaAHzoXeEhKSMwU92CiEoHEFlsZeyvGgUFu9kosLelpyJvEKupwqQeuo3c6CWVahoA3JyJmCRu492CgMTrb7QQWgHBdBz40naPdyrgh/FnEUVdCpQKKMc7xZJZrksCIO13KmoE2j0Jyv7SOHrqOSfqQCUI4FeX/Fy1UjQXcp3unJ2RZ0POJQNabnOACIkpMcsQzaoZwp6HDle3XL2iisY9zurQpAORZ4tY6HCpqHJBnBQsTTkfPyRyrorAo6TYig31qHJtmCdi1nCpqTHNYlsrihptnd+kIFoHz2yHjdXUYcRtAkeTmTkYKeT5ugVdBPeCZH7IZvZ3KOCxqTHLhEtt5ho1BkvaACUI7DmreXLQdXXXHMrkRBg6TlHBc0NwnTJmidhb4/cD+hqaIhaPdyBuuc5Bg+ehTPuyoA5bNH5HjGCNrEHEWz6k0SljPBe6XBCloopytr1FE7uUD2KYSJHBqCZsSRjJzZKMRN6WGjcBkZNKKOxyoA5ViQLTd/y1Za1iQHq+jE5UyGKmiM2S2ooFOFiPZ8JGjIs20JOhE5U9D2yncbK9+oondVAMqxIFdp3RNJm5ijxpiDJCvnTfNkBd2SJlAtpYLWSQ6pZHtWo5AZdFJypqCDRmEwycFGob+8riOYyueP53euZSBoTHOYZuHU5EwoaI7ZpVPQ2ih8ZTUKKehE5ExBRyvfAxuFkPUNFYDy2fNSDkHPlBrvkUXnUUWbLHo6cm6Hz2gOutKioBfzleeqxNQJ+ie7UQg5G9zLmaztNwpLVqMwX23dUwEoxyXmuCOS7ssTVTQFnbScqwIzaMm/c4OCfqZKTN0kxx007EzMERO0azlT0DiLuhbl0GwU1vUPcOV4gMtRRdAfkEXnOXLXpZATkjMFjQP7C7KIkC03IGaM2eF2bO3UpwwR5iUIGpEDxGkJ2rmcyVqUQ7NRCDkj5uitvdjVk+2U44FMcty1q2hUtJByuSU4lzOx82dki8ieIWi89lWJ6UKGNk6xgoagN36noJfltWM5xxuFWFipDSysXFMBKMeC5tqLU5li/UMGVbQ1F20knZCc49VzuEVYQh59QZWYPkTQb8KDk+wcOik5U9Dm6FHk0CtBDp0xOXRDBaAcpyz6NqpoNgzrImlTRbvLnJk7U85StTeZPWuDMN1IFrwhQJgUNCtox3JmxGEahVxY8arMobVPoRwvRNDPTNTB5RVTRYMJpzXs1e5Ss2vLGdFGJOieNIK+UhWmE8mCa/uNwh3ImaN2ycgZvMTkCBdWmEMX/N5rmUJSASjHBsmDzy4V63tmNtqqpBvduKQp5wNBMSPf9qpm6cCWs8me63dUg+lFRHm9JlFD3eTQFDQraNdypqBNo9A+2Q4TP8V65/jdsKJo1LEkGZ+ppJvWfDSqaebSo2VNzHtsMjYgZskPUf2YJg8mNSBmlfMM0d3+52kfgmYOzTXvRORM7EYhF1aO78FJikYd9xcLPqTZz0bVtA9RdziGB0rASBuY1+F7qJQh9oLfCRqPuTLFzKo5pCdyvq36mw1E0G9qHZ7LQUEnJ2erUTh4sh0q6ScqAOW4SroGSS9FokZFbUUfHvCBSFvAE5/LB7QgdXN2QqmOU+rQ/BsUc9YspEimeE61N1OTHA+tmINVdIJyBmwUVniyHeah/T2cE6ICUI7raXd3FvLV3mK+iso3kDXiDwg7Ez1LAfycYH6cVx0hZYp5J1uqf6+6mz1EzLf8QNBWzCEkJmcKGifbmRy6YB2cVKp3dCRTOb5IU+ZLEe7PgWiFhQiR76KFfMz3KONBeiL5R5IfamNnhhGZnvclC7bmoSHoxORMutHJdvYNK748W7rUpOhfMn96XkTdFBE/n8sWe7jYVZ5kPkaphwhD4o2GRCPXOhs7n89qrubQH8J5aIgYVTRISs4EgvbbzKHR08AZ0RsqAEWxgGxFupck1rgpeCJhX6KPHyS2uCmVzYVSfeWMauzzRZp1T3DCXIM5NCOOZOTMmGPbXliBoBFzfFABKIqi7G8U1gZzaFNFtzYSkzMbhTzZDjl00ID2sQJ+XgWgKIpiFlauQNCQZbNLQeOZlJwpaNMo3AgXVsIcutoKZ+gVRVH0iNqT1fZmbzCHNiQiZwp6P4cuhZc8xHNoRVEUbRS+Qh4MYUKyVgadkJzBCyNo5NCNLhuFkkO/VwEoiqJwYWXzftVa+25R0InImdSjhRXesIIcuorJDl14UhRFASLmmzgbYyjmwDMRORM2CmVhxeTQEDRy6FsqAEVRFGFl691pNOvCmMPImIJOSM6MObYGDk7CRmG2XP9ZBaAoihIigt6pWuN2kDMknaScARdWmtIo3M+h36oAFEVR9huF96Iqutm1BZ2YnCloLqyEN30j5qi0umdVAIqiKOaGlTkcXrR//OgOI46k5ExBrzy1b1iBoDHVcVMFoCiKIvTlyikR9J61VUhBA8dyJvWoUWhuWOHVably44EKQFEUZT+HfsJpDhEqG4Tu5UzqVqOw1IhyaF+ovVIBKIqi7B+cdLvSGow5QBO4lTOpc2FFNgqbwcl2vK2ntbZ9SgWgKIoitNZefoksWKY5uFVoRRyu5UxBc6NQvrYXLax4yKGXF1QAiqIoIZIFv4nFHBS0Uznbgh61sIIcuqkCUBRFCRE5P8A0h99hFU1BJyFnW9DVZS6shPdn1rZVAIqiKPsXyS6YmGPDTHN0IeEd4FbOZNsIGo1C62Q7CHrRq/Rey3SJCkBRFCUct5OlkV2pZtksNDLeSUbORtBWo7DLjULcj1nw23qRrKIoSoRkwT+iivbZLKSgXcuZGEHzCiw0ClFBI+7wVACKoij7W4XfQJTVZTYLIegE5UxBc6MwWw4EjeddFYCiKIpFWYwMWUKag1V0MnLmyjcnORoQNKKOrgpAURTFQiY5fJF0nyN3zKKBezlz1C6Y5AgEjVE7wddJDkVRFBsZrTsjMUcv2iyEYCln4FbOw6N2nIWWjcIRR48qiqJozPFE4MidyaITkjMraBNxIIPGFMdCvtxXASiKosSXVm7K2BtyYWbRlqQdyZnVM8bseC60GbMrI4ceVUEriqLoTLQsjryHNK0smoJ2JudogmN53cQb2CT0qqiekUE/UQEoiqKMQCY57mC7T55c/2aD0Imct0I5I3sOjhuFlBlvyBy0HtyvKIoyij+kipaq9r01Fw0BQ9BOMmdIHxGKkTM3CMPqubotVfy/qAAURVE+UUWXw+1CVL31Cac1UDUjNkETEmN1lpyFMp675cbqQe4lVBRF0Sq61OgiJw7kWouE2wWU82ghB2wNiBmRSVGk7/ntcKTOp5yleu551dYVFYCiKMrBRu7uoIEXTXVAspiPhnSNrPG0WKGQhU1IGXJHxQwxY50b+XJ4e0qFsQYqZ6mmr6kAFEVRxqiiZT75rdCHqMuRqJc3cEyoxQagkBGL4MdC7vi5GKHLGTFjUoNVc5g5/6qxhqIoyiEQ4V6Q6raH0+YiUQPk06BYByukUGtDyDjfGavbWD4RMfuomG0x4+P3EnN42hBUFEWZABFvQSrgntDPW+RAGTRC6v1sqWYq5UKVUh4Uc/U3EfedHT2UX1EUxQ1SLc9JNfwW8kVFnCn4ImFQldgCVGJCtqSMn/dQqnBtAiqKoiTBLjJpv31DRL0hYn4LEQ/Rk8/viIwfZ0v1+1Jhe+XmEeXLiqIoiqLoX/8fiSBbS741STAAAAAASUVORK5CYII=');background-size:180px 110px;background-repeat:no-repeat} .logo div{text-align:right;padding-top:6px;padding-right:5px;font-style:italic;font-size:10px} .color1{color:#FFFFFF} .color2{color:#264666} .color3{color:#264666} .color4{color:#FF0000} .color5{color:#66FF00} .color6{color:gold} .color7{color:#000000}.cross{height:22px;width:22px;background-image:URL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAMAAADzapwJAAAAilBMVEUAAAC4t63bkZHbj4/bjo7bjIzbiorbh4fbhYXbg4PbfHzbdXXbdHTbamrbZ2fbYWH/X1//YmL/WFj/bm7/cHD/cnL/dXX/enr/e3v/gID/jo7/mJj/qKj/q6v/tbX/uLj/ubn/v7//y8v/zMz/z8//2dn/29v/3Nz/5ub/5+f/6en/8vL/8/PBRkaL6mB7AAAAAnRSTlMAgJsrThgAAACtSURBVHherc83DsJAAERRFpxzzjmnuf/1wFj2LnRITPX1urn9bwAtRu930Lr0sa3c28Gt2+N08Ms0Cy+HME8LfzKBOPaDBEhDP4ogt8vlrmnatmk6eVfqSl2VZVUrh1Iv8izLi0Pp4FuOY/n4UtO1Lct2zQ+HEXmu77teZDAOPQ4iEzCjINYvh5aEsQFCYMRhop0ONU30/QWBnqQqfalqIEdpKvsShK3LCVO/7wnVFRLHRvPhcwAAAABJRU5ErkJggg==');display:inline-block;vertical-align:middle}.tick{height:20px;width:22px;background-image:URL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAABMElEQVR4XuXRS0vDQBQF4P5ftwIKCCIg4raCKKjW90ZBQRLf7mwebURQEBGBJqlqUlPTltpQjz0K2Qw61k5XvTAcTm7yMZDMYM7MzhD6gp47W2AqRzkprhDlqMGJnjkbaLdb3+cj6fRNotC17fF/o6fFNbSSGt6TmNnp67+jXMrQk8IKGs0Q9WbAZJehvMkqmD+hx/YyqnUfUc1lsstQfrSEasPDkb0o4OyH1gLC+B4vb3dM9r+gOVTiBzxHNzzQzfkUZ+rmHJ6ia5Rfr5g44F72o7gkwpf9ShGlwIAbmNjPZ4kz2fkcXmhDM2cFVIrv5idw+TgNyxuD4Y2mh53PuRfQbnCnPAXDHcFFaZjJLkG7vHnB/8peUBHfMyZxG+aYAtozrllZAVWCC6hKPDMw8wlBVbsPzqUrcgAAAABJRU5ErkJggg==');display:inline-block;vertical-align:middle}.warn{height:22px;width:22px;background-image:URL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAADW0lEQVR4XqWSW2gcVRiAv3PmbGZ30p3ZTbqbELIVaoyuBqmRWFRDAaVKpGitUPBabAGtN8DGbAX6qi+CiqgiIPhWfBEKeAMErCggouCLb9JimzbZdHPZy9zOccihWJGYtP3gYwbOzPf/DCMAfv5QoBND6IEbgdRYUpAGOhEqL3kP6HiV2rGodRZtwAggEwUASQpSCqaOGiRboFLb/en4wdMvjB347jXgXTZnS+H7K5ONp4rDYwQ7Jtg++dYrwB3XHS7W9jX84Z3o9pnMvyjv2EWhOjV3veE95Zsff8CRIUZ3MWkb1Scp1Z8/CNx+zeFgdO/xYHgU4kWcvMZxNSZqZltP0D+0a+5aw9OlG2ceVKpLc6nJ11/9xOnvfyEKV8i5kmD88BPAxFWH/eHpRlCtQrrIuXPneWj/cab3vkq3uwpxk4Fanf7KrbNXG76ndMOeGYdl0Ks4MgTg7rvqCNOFdJlcn6Y09uwzQH3LYb862fAHA3Q0D1kI08OibThTxy2C6iCez+xWw7v9kTv3KS6BboNZszFAoMG0wW6NbH2LV6ocAsY3DfvbxxtBOY+OLyLogF6zIcAYDSZaH6YXvyFtniTwA+Ft81/fLDyVhR91ZGv9ZbtxmzSx4R/POpjoPDS/wKx8jshBn1zAKxYPAWMbhkfUaMMvKUzSRNDL7ELaplpOOfHyTXzyUptC5wPo/IBQAqkAs4zfHzuel5/dKDzpDvQ/ptKLmGQJ4uwanoH276ws/EYcR4RRjzB0QYLAZNpCzmlR8NzngJ1YUFjIe7m5gvyTuPUHTh8IBdIBCnDylOLNjxL7y91WYbLewRjWJdOkEdsKSrVcdSyO06NXho8UPfGIQ4TRrKszAXQPnp7xGSobRockt9S6JB17rq94VokeRc95cmmZU8CXCkBI90ChkHd10sKCHSAhiWDEX+LIw4CBMIL48tAUawIm0RS8wGdlbT/ENpyTvfdjPXivFwwVpdAIB6QEMiWQkplgcUA6/9wLnalAG0m0tDyvxKWPAQQWPnvDua9UrL4YpmogTsgj6LMZhPVfGEDbmYT5nOi4KlyYv3DhncNv86sN/we/CGIQ8EF4YFy7k5FYUiABekAbaIFehNUQiwCM4H8pKxsVTqYAc/nrp5kJtFI24G8rjV26nVE/FAAAAABJRU5ErkJggg==');display:inline-block;vertical-align:middle}.p5{padding:5px}</style></head><body><div class="logo color3"><div>SAML Test v$iv</div></div>~}sub pagefoot{print qq~<hr><footer>For assistance, please visit <a href="https://mid.as/saml-integration" target="_blank">mid.as/saml-integration</a></footer><hr></body></html>~}sub splash{my$nonetsaml2;eval{require Net::SAML2};if($@){$nonetsaml2=1}pagehead();print qq~<form name=f method="post" action="$fname"><input type=hidden name="act" value="start"><input type=hidden name="nolog" value="$nolog"><h2>IS YOUR SERVER READY FOR SEAMLESS SSO INTEGRATION?</h2><div class="box color1" style="width:100%;max-width:600px;margin-left:auto;margin-right:auto"><div style="width:100%;height:300px;overflow:auto;font-family:Arial;font-size:1em;text-align:justify;padding-right:5px">This tool will allow you to test your server setup to determine whether you're able to integrate MIDAS with your SAML service, to allow users to be authenticated and seamlessly logged into your MIDAS system upon each access.<br><br>THIS TOOL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL THE AUTHOR BE LIABLE TO YOU FOR INDIRECT, SPECIAL, OR CONSEQUENTIAL DAMAGES, ARISING OUT OF ANY USE THEREOF OR BREACH OF ANY WARRANTY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.<br><br>Click "Continue" to begin.</div>~;if($nonetsaml2){print qq~<div class=color4>The Perl module Net::SAML2 was not detected on this server<br>Please <a href="https://mid.as/kb/00129/how-to-install-perl-modules" target=_blank>install this module</a> then reload this page to continue</div>~}else{print q~<button>Continue <span class=arrow_right></span></button>~}print q~</div></form>~;pagefoot()}sub samlsetup{pagehead();my$idp_meta=SSI_INCLUDE("$cwd/SAML/$certprefix-idp-meta.dat")||SSI_INCLUDE("$cwd/SAML/MIDAS-idp-meta.dat");my$idp_cert=SSI_INCLUDE("$cwd/SAML/$certprefix-idp-cert.dat")||SSI_INCLUDE("$cwd/SAML/MIDAS-idp-cert.dat");my$sp_key=SSI_INCLUDE("$cwd/SAML/$certprefix-sp-key.dat")||SSI_INCLUDE("$cwd/SAML/MIDAS-sp-key.dat");my$sp_key_ph=($sp_key)?'[Not Shown]':q~Paste Service Provider's Private Key Here...~;my$sp_cert=SSI_INCLUDE("$cwd/SAML/$certprefix-sp-cert.dat")||SSI_INCLUDE("$cwd/SAML/MIDAS-sp-cert.dat");my$acsurl=($midas->{paths}{aliasurl})?"https://$midas->{paths}{aliasurl}$fname":"https://$midas->{paths}{weburl}$fname";$acsurl='https://'.$ENV{SERVER_NAME}.$ENV{REQUEST_URI} if !$acsurl;print qq~<script>document.addEventListener('DOMContentLoaded', ()=>{const genCertKeyButton=document.getElementById('gen_cert_key');const saveButton=document.getElementById('saml_save');genCertKeyButton.addEventListener('click', (event)=>{event.preventDefault();genCertKeyButton.innerHTML='Generating...';fetch('$fname', {method: 'POST',headers: {'Content-Type': 'application/x-www-form-urlencoded',},body: 'act=gen_cert_key',}).then(response=>response.text()).then(data=>{const r=data.split('|');document.getElementById('saml_sp_key').textContent=r[0];document.getElementById('saml_sp_cert').textContent=r[1];genCertKeyButton.innerHTML='Generate'}).catch(error=>{console.error('Error:', error);genCertKeyButton.innerHTML='Error Generating'})});saveButton.addEventListener('click', (event)=>{event.preventDefault();saveButton.innerHTML='Saving...';formData=new FormData(document.getElementById('saml_info'));formData.append('act','samlsave');const params=new URLSearchParams();for (const [key, value] of formData.entries()){params.append(key, value)}fetch('$fname', {method: 'POST',headers: {'Content-Type': 'application/x-www-form-urlencoded'},body: params.toString()}).then(response=>response.text()).then(data=>{saveButton.innerHTML='Saved';if(confirm("Settings Saved - Would you like to perform a SAML test now?")){self.location.href="$fname?act=test"}}).catch(error=>{console.error('Error:', error);saveButton.innerHTML='Error Saving'})})});</script><form id=saml_info><fieldset class=box><legend>Identity Provider (IdP) Settings</legend><div style="display:grid;grid-template-columns:100px 1fr;grid-gap:5px;align-items:center"><div class=r>Metadata:</div><div><textarea name=saml_idp_meta class="width100 font08" style="word-wrap:normal;height:80px" spellcheck=false placeholder="Paste URL or Raw Metadata Here...">$idp_meta</textarea></div><div class=r>Certificate:</div><div><textarea name=saml_idp_cert class="width100 font08" style="word-wrap:normal;height:80px" spellcheck=false placeholder="Paste Identify Provider's Certificate Here...">$idp_cert</textarea></div></div></fieldset><fieldset class=box><legend>Service Provider (SP) Settings</legend><div style="display:grid;grid-template-columns:100px 1fr 140px;grid-gap:5px;align-items:center"><div class=r style="grid-column:1 / 1;grid-row:1 / 1">Assertion Consumer Service (ACS) URL:</div><div class="l color6" style="grid-column:2 / 3;grid-row:1 / 1">$acsurl</div><div class=r style="grid-column:1 / 1;grid-row:2 / 2">Private Key:</div><div style="grid-column:2 / 2;grid-row:2 / 2"><textarea name=saml_sp_key class="width100 font08" style="word-wrap:normal;height:80px" spellcheck=false id=saml_sp_key placeholder="$sp_key_ph"></textarea></div><div class=r style="grid-column:1 / 1;grid-row:3 / 3">Certificate:</div><div style="grid-column:2 / 2;grid-row:3 / 3"><textarea name=saml_sp_cert class="width100 font08" style="word-wrap:normal;height:80px" spellcheck=false id=saml_sp_cert placeholder="Paste Service Provider's Certificate Here...">$sp_cert</textarea></div><div style="grid-column:3 / 3;grid-row:2 / 4">~;print q~<button id=gen_cert_key>Generate</button>~ if $midas->{paths}{openssl};print q~</div></div></fieldset><button id=saml_save>Save <span class=arrow_right></span></button></form>~;pagefoot()}sub gen_cert_key{unlink "$cwd/SAML/$certprefix-sp-key-temp.dat";unlink "$cwd/SAML/$certprefix-sp-cert-temp.dat";my$dataname;my$dbs;eval{$dbs=@{$midas->{database}{db}}};if($dbs){foreach my$k(0..@{$midas->{database}{db}}-1){if($midas->{database}{default} eq $midas->{database}{db}[$k]{r}){$dataname=$midas->{database}{db}[$k]{display};last}}}$dataname='MIDAS' if !$dataname;my$country=$midas->{languages}{default};$country=~s/\w\w-//;my$aliasdomain=$midas->{paths}{aliasurl};$aliasdomain=~s!/(.*)!!;my$subject='/C='.$country.'/O='.$dataname.'/CN='.$aliasdomain;my$sp_key;my$sp_cert;if(-e "$cwd/SAML/$certprefix-sp-key-temp.dat"){$sp_key=SSI_INCLUDE("$cwd/SAML/$certprefix-sp-key-temp.dat");$sp_cert=SSI_INCLUDE("$cwd/SAML/$certprefix-sp-cert-temp.dat");unlink "$cwd/SAML/$certprefix-sp-key-temp.dat";unlink "$cwd/SAML/$certprefix-sp-cert-temp.dat"}else{require LWP::UserAgent;LWP::UserAgent->import();eval{require IO::Socket::SSL};if($@){$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0}my$ua=LWP::UserAgent->new(env_proxy=>0,keep_alive=>0,timeout=>30,agent =>"SAML TEST v$iv");if($midas->{paths}{proxy}){$ENV{HTTPS_PROXY}=$midas->{paths}{proxy};$ua->proxy(['http','https'],$midas->{paths}{proxy})}my$r=$ua->post('https://u.mid.as/SAMLcertgen.pl',[dataname=>$dataname,prefix=>$certprefix,country=>$country,alias=>$aliasdomain]);my@response=($r->is_success)?($r->code,$r->status_line,$r->content):($r->code,$r->status_line);if($response[0]==200){if($response[2]=~m/^-----BEGIN PRIVATE KEY-----/){my@spdata=split(/\|/,$response[2]);$sp_key=$spdata[0];$sp_cert=$spdata[1]}else{$sp_key=$sp_cert='Unable to generate'}}}print "Content-type: text/plain; charset=UTF-8\n\n$sp_key|$sp_cert"}sub samlsave{if(!-e "$cwd/SAML"){mkdir "$cwd/SAML/"}if($in{saml_idp_meta}){$in{saml_idp_meta}=~s/\r//g;$in{saml_idp_meta}=~s/&(?!amp;)/&amp;/g;open(my$TMPF,'>:encoding(UTF-8)',"$cwd/SAML/$certprefix-idp-meta.dat");flock($TMPF,2);print $TMPF $in{saml_idp_meta};flock($TMPF,8);close($TMPF)}if($in{saml_idp_cert}){$in{saml_idp_cert}=~s/\r//g;open(my$TMPF,'>:encoding(UTF-8)',"$cwd/SAML/$certprefix-idp-cert.dat");flock($TMPF,2);print $TMPF $in{saml_idp_cert};flock($TMPF,8);close($TMPF)}if($in{saml_sp_key}){$in{saml_sp_key}=~s/\r//g;open(my$TMPF,'>:encoding(UTF-8)',"$cwd/SAML/$certprefix-sp-key.dat");flock($TMPF,2);print $TMPF $in{saml_sp_key};flock($TMPF,8);close($TMPF)}if($in{saml_sp_cert}){$in{saml_sp_cert}=~s/\r//g;open(my$TMPF,'>:encoding(UTF-8)',"$cwd/SAML/$certprefix-sp-cert.dat");flock($TMPF,2);print $TMPF $in{saml_sp_cert};flock($TMPF,8);close($TMPF)}print "Content-type: text/plain; charset=UTF-8\n\n1";exit}sub samltest{eval{require Net::SAML2};if($@){ssoerr('Net::SAML2 not found')}my$issuer='midas';my$provider='midas';my$idp_meta="$cwd/SAML/$certprefix-idp-meta.dat";my$idp_cert="$cwd/SAML/$certprefix-idp-cert.dat";my$sp_key="$cwd/SAML/$certprefix-sp-key.dat";my$idp_metadata=SSI_INCLUDE($idp_meta);if(!$idp_meta){ssoerr('Identity Provider metadata not found')}my$xml_doc;if($idp_metadata=~m/</){eval{$xml_doc=XML::LibXML->load_xml(location=>$idp_meta)};if($@){ssoerr('Invalid Identity Provider metadata')}}else{require LWP::UserAgent;LWP::UserAgent->import();$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0;my$ua=LWP::UserAgent->new;$ua->timeout(10);my$response=$ua->get("$idp_metadata");$response=$response->content;eval{$xml_doc=XML::LibXML->load_xml(string=>$response)};if($@){ssoerr('Invalid Identity Provider metadata')}}$idp_metadata=$xml_doc->documentElement;my $idp=Net::SAML2::IdP->new_from_xml(xml=>$idp_metadata,cacert=>$idp_cert,);my $authnreq=Net::SAML2::Protocol::AuthnRequest->new(issuer=> $issuer,destination=>$idp->sso_url('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'),provider_name=>$provider,);my$saml_request_id=$authnreq->id;if(!$saml_request_id){ssoerr('SAML Request ID not found')}print "Set-Cookie:SAMLID=$saml_request_id; HttpOnly; SameSite=Strict; Secure\n";my $redirect=Net::SAML2::Binding::Redirect->new(key=>$sp_key,cert=>$idp->cert('signing'),param=>'SAMLRequest',url=>$idp->sso_url('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'),);my$url=$redirect->sign($authnreq->as_xml);if(!$url){ssoerr('Redirect URL not found')}print "Location:$url\n\n"}sub samlresponse{my$saml_response=$in{SAMLResponse};my$op;$op.=qq~<fieldset calss=box><legend>SAML OUTPUT</legend><textarea class=width100 style="height:200px">~;my$tryagain;if($in{action} eq 'verify'){eval{require Net::SAML2};if($@){pagehead();print qq~<fieldset calss=box><legend>SAML OUTPUT</legend><textarea class=width100 style="height:200px">Net::SAML2 Not Found</textarea></fieldset>~;pagefoot();exit}require Net::SAML2::Binding::POST;require Net::SAML2::Protocol::Assertion;$op.=qq~Got SAML response and action is "verify"\n~;my$issuer='midas';my$idp_cert="$cwd/SAML/$certprefix-idp-cert.dat";my%cookies=getCookies();my$saml_request_id=$cookies{'SAMLID'};$op.=qq~Got SAML request id: $saml_request_id\n~;my$post=Net::SAML2::Binding::POST->new(cacert=>$idp_cert );my$ret=$post->handle_response($saml_response);my$assertion=Net::SAML2::Protocol::Assertion->new_from_xml(xml=>decode_base64($saml_response));my$valid=$assertion->valid($issuer,$saml_request_id);if($valid){my$dn=determine_name($assertion);my$em=determine_email($assertion);if(($dn)&&($em)){$op.=qq~========================================\nSUCCESS! I received the user/email: $dn <$em>\n========================================\n~}else{require Data::Dumper;my$raw=Data::Dumper::Dumper($assertion);$op.=qq~VALID RESPONSE - but unable to determine name/email\n$raw~}}}else{$op.=qq~Got SAML response but action is NOT "verify":\n$saml_response\n~;$tryagain=1}$op.=q~</textarea></fieldset>~;if($tryagain){$op.=qq~<form method=post action="$fname" style="display:none"><input type=hidden name=action value='verify'><textarea name=SAMLResponse>$saml_response</textarea><script>document.forms[0].submit()</script>~}else{print "Set-Cookie:SAMLID=; HttpOnly; SameSite=Strict; Secure\n"}pagehead();print $op;pagefoot()}sub determine_email{my$assertion=shift;my$email=$assertion->attributes->{email}[0]||$assertion->attributes->{Email}[0]||$assertion->attributes->{EmailAddress}[0]||$assertion->attributes->{mail}[0]||$assertion->attributes->{'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'}[0];return $email}sub determine_name{my$assertion=shift;my$name;my$firstname;my$surname;$firstname=$assertion->attributes->{firstname}[0]||$assertion->attributes->{firstName}[0]||$assertion->attributes->{FirstName}[0]||$assertion->attributes->{givenname}[0]||$assertion->attributes->{GivenName}[0]||$assertion->attributes->{givenName}[0];$surname=$assertion->attributes->{lastname}[0]||$assertion->attributes->{lastName}[0]||$assertion->attributes->{LastName}[0]||$assertion->attributes->{surname}[0]||$assertion->attributes->{Surname}[0]||$assertion->attributes->{sn}[0];$name=($surname)?$firstname.' '.$surname:$firstname;$name=$assertion->attributes->{'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name'}[0] if !$name;return $name}sub ssoerr{my$err=shift;pagehead();print qq~<h1>err</h1>~;pagefoot();exit}sub getCookies{my%cooks;foreach(split(/; /,$ENV{HTTP_COOKIE})){my($key,$val)=split(/=/,$_,2);$cooks{$key}=$val if defined $key}return %cooks}sub SSI_INCLUDE{local$/;open(my$F,'<',shift);my$c=<$F>;close($F);return $c}sub parse{my(@pairs,%in);my($buffer,$pair,$name,$value);if($ENV{REQUEST_METHOD}eq'GET'){@pairs=split(/&/,$ENV{QUERY_STRING})}elsif($ENV{REQUEST_METHOD}eq'POST'){read(STDIN,$buffer,$ENV{CONTENT_LENGTH});@pairs=split(/&/,$buffer)}PAIR:foreach my$pair(@pairs){($name,$value)=split(/=/,$pair);$name=~tr/+/ /;$name=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;$value=~tr/+/ /;$value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;($value eq "---") and next PAIR;exists $in{$name}?($in{$name}.="~~$value"):($in{$name}=$value)}return %in}