Changeset 3780


Ignore:
Timestamp:
11/04/08 16:26:31 (12 years ago)
Author:
pjkersha
Message:

Working version of NDG Security Perl client - acts as a client to Single
Sign On and Gatekeeper.

Location:
TI12-security/trunk/perl
Files:
5 added
2 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/perl/NDG/Security/Client.pm

    r3775 r3780  
    88use CGI qw/-debug/; 
    99use Log::Log4perl; 
    10  
    11 Log::Log4perl::init('/var/www/cgi-bin/NDG/Security/ndg-security-log.cfg'); 
    12  
    13 my $log = Log::Log4perl->get_logger; 
    14  
     10use AppConfig qw/:argcount/; 
     11     
     12 
     13#Log::Log4perl::init($ENV{'NDGSEC_PERL_CLNT_LOGFILE'}); 
     14#my $log = Log::Log4perl->get_logger; 
     15my $log; 
    1516my %SECURITY_ARG = ('h'=>1, 'roles'=>1, 'sid'=>1, 'org'=>1, 'u'=>1); 
    1617 
     
    2021    # Constructor 
    2122    my $class = shift; 
     23    my $cfgFilePath = shift;  
    2224    my $cgi = shift; 
    23     my $encryptionKey = "123456789"; 
     25 
     26    # Create a new AppConfig object 
     27    my $cfg = AppConfig->new(); 
     28     
     29    # define all the variables we will use, with defaults were necessary 
     30    $cfg->define( 
     31        'logCfgFilePath' => {ARGCOUNT => ARGCOUNT_ONE}, 
     32        'encryptionKey' => {ARGCOUNT => ARGCOUNT_ONE}, 
     33        'cookieName' => {ARGCOUNT => ARGCOUNT_ONE}, 
     34        'wayfURI' => {ARGCOUNT => ARGCOUNT_ONE}, 
     35        'pepCfgFilePath' => {ARGCOUNT => ARGCOUNT_ONE} 
     36    ); 
     37 
     38    # Read configuration file 
     39    $cfg->file($cfgFilePath); 
     40     
     41    # Initialise logger 
     42    Log::Log4perl::init($cfg->logCfgFilePath); 
     43    $log = Log::Log4perl->get_logger; 
     44      
    2445    my $self = { 
    2546        "cgi"  => undef, 
    26         "encyptionKey" => $encryptionKey, 
    2747        "cipher" => undef, 
    28         "cookieName" => "ndg-security", 
    29         "wayfURI" => "https://localhost/sso/wayf", 
     48        "cookieName" => $cfg->cookieName, 
     49        "wayfURI" => $cfg->wayfURI, 
    3050        "b64encReturnToURL" => undef, 
     51        "pepCfgFilePath" => $cfg->pepCfgFilePath, 
    3152    }; 
    3253 
     
    3758 
    3859    # Cipher for encryption of session cookie 
    39     $self->{cipher} = new Crypt::CBC(-key=>$encryptionKey); 
     60    $self->{cipher} = new Crypt::CBC(-key=>$cfg->encryptionKey,  
     61                                     -cipher=>'Blowfish',  
     62                                     -salt=>1); 
    4063     
    4164    # Supply encoded form of return to URL ready to be passed to SSO Service for user login 
     
    4366     
    4467    return $self; 
     68} 
     69 
     70 
     71# Class method 
     72sub _readCfgFile 
     73{ 
     74    my $cfgFilePath = shift; 
     75     
     76    # create a new AppConfig object 
     77    my $config = AppConfig->new(); 
     78 
     79    # read configuration file 
     80    $config->file($cfgFilePath); 
     81     
     82    return $config 
    4583} 
    4684 
     
    70108        # nph flag crashes with Apache - intended for MS IIS? 
    71109        return $self->{cgi}->redirect(-uri=>$returnToURL, -cookie=>$cookie); 
     110    } 
     111    elsif (! $self->_getSessionFromCookie()) 
     112    { 
     113        $self->_makeHttpsReturnToURL(); 
     114        my $wayfURI = $self->{wayfURI}."?r=".$self->{b64encReturnToURL}; 
     115        $log->info("User not logged in - Generating redirection header for WAYF: ". 
     116            $wayfURI."..."); 
     117             
     118        return $self->{cgi}->redirect(-uri=>$wayfURI); 
    72119    } 
    73120    else 
     
    78125        # URL is set to https to ensure encrypted channel for SSO service -> to THIS  
    79126        # SSO client transfer 
    80         my $queryStr = $self->{cgi}->query_string(); 
    81         my $returnToURL = "https://" . $virtualHostName . $urlPath ."?" . $queryStr; 
    82          
    83         $log->info("Generating return to URL with SSL transport ".$returnToURL."..."); 
    84          
    85         $self->{b64encReturnToURL} = urlSafeB64Encode($returnToURL); 
     127        $self->_makeHttpsReturnToURL(); 
    86128        return ''; 
    87129    } 
    88130} 
    89131 
     132 
     133sub _makeHttpsReturnToURL 
     134{ 
     135    my $self = shift; 
     136    my $virtualHostName = $self->{cgi}->virtual_host(); 
     137    my $urlPath = $self->{cgi}->url(-absolute=>1); 
     138     
     139    my $returnToURL = "https://" . $virtualHostName . $urlPath; 
     140    my $queryStr = $self->{cgi}->query_string(); 
     141    if ($queryStr) 
     142    { 
     143         my $returnToURL .= "?" . $queryStr; 
     144    } 
     145     
     146    $log->info("Generating return to URL with SSL transport ".$returnToURL."..."); 
     147     
     148    $self->{b64encReturnToURL} = pyUrlSafeB64Encode($returnToURL); 
     149} 
    90150 
    91151sub _stripSecurityQueryArgs 
     
    112172 
    113173 
    114 sub _makeCookie 
    115 { 
    116     my $self = shift; 
    117     my %session = shift; 
    118     my $serializedSess = freeze(\%session); 
    119      
    120     my $encrSess = $self->{cipher}->encrypt_hex($serializedSess); 
     174sub _makeCookieFromSession 
     175{ 
     176    my $self = shift; 
     177    my $session = shift; # ref to hash 
     178    my $serialisedSess = freeze($session); 
     179     
     180    my $encrSess = $self->{cipher}->encrypt_hex($serialisedSess); 
     181    $log->info("Encrypted session is: ".$encrSess); 
    121182    my $cookie = $self->{cgi}->cookie( 
    122183        -name=>$self->{cookieName}, 
     
    130191 
    131192 
    132 sub _getCookie 
    133 { 
    134     my $self = shift; 
    135      
    136     my $cookie = $self->{cgi}->cookie($self->{cookieName}); 
    137     my $serialisedSess = $self->{cipher}->decrypt_hex($cookie); 
    138     my %session = thaw($serialisedSess); 
    139     return %session; 
     193sub _getSessionFromCookie 
     194{ 
     195    my $self = shift; 
     196     
     197    my $cookie = $self->{cgi}->cookie($self->{cookieName}) || return undef; 
     198    $log->debug("Cookie content is: ".$cookie); 
     199     
     200    my $serialisedSess; 
     201    eval  
     202    { 
     203        $serialisedSess = $self->{cipher}->decrypt_hex($cookie) 
     204    }; 
     205    if ($@) 
     206    { 
     207        die "Getting user session details: ".$@; 
     208    } 
     209    my $session = thaw($serialisedSess); 
     210    my $sessionMsg; 
     211    my $key; 
     212    my $value; 
     213     
     214    while(($key, $value) = each(%{$session})) 
     215    { 
     216        $sessionMsg .= "$key=$value\n"; 
     217    } 
     218    $log->debug("Retrieved session from cookie: ".$sessionMsg); 
     219     
     220    return $session; 
    140221} 
    141222  
     
    155236        u => $cgi->param('u'),  
    156237        org => $cgi->param('org'),  
    157         roles => @roles); 
    158          
    159     return $self->_makeCookie(%session); 
    160 } 
    161  
     238        roles => \@roles); # reference - set itself messes up hash 
     239         
     240    my $sessionMsg; 
     241    my $key; 
     242    my $value; 
     243    while (($key, $value) = each(%session)) 
     244    { 
     245        $sessionMsg .= "$key=$value; "; 
     246    } 
     247    $log->debug("Setting session: ".$sessionMsg); 
     248     
     249    return $self->_makeCookieFromSession(\%session); 
     250} 
     251 
     252 
     253# Policy enforcement Point - provide access control decision given resource constraints 
     254# and user attributes 
     255sub pep 
     256{ 
     257    my $self = shift; 
     258    my $resrcFilePath = shift; 
     259     
     260    # Retrieve user credentials 
     261    my $session = $self->_getSessionFromCookie(); 
     262     
     263    my $msg = "resource ".$resrcFilePath." for user ".$session->{u}." with session ID = ".  
     264        $session->{sid}; 
     265     
     266    # Gather access constraint information for resource 
     267    my @accessInfo = getFTPAccessFileReadPermissionsInfo($resrcFilePath); 
     268    if ($accessInfo[0]) 
     269    { 
     270        # Access may be granted if read permission is set to public or if the file 
     271        # is previously cached 
     272        $log->info("Access granted for ".$msg.": ".$accessInfo[1]->{msg}); 
     273        return 1; 
     274    } 
     275     
     276    # Returns a hash containing (boolean, message) 
     277    $log->debug("Calling pyPEP ..."); 
     278    my $decision = pyPEP($accessInfo[1], $session, $self->{pepCfgFilePath}); 
     279    if ($decision->{accessGranted}) 
     280    { 
     281        $log->info("Access granted for ".$msg); 
     282    } 
     283    else 
     284    { 
     285        $log->info("Access denied to ".$msg.": ".$decision->{msg}); 
     286    } 
     287     
     288    return $decision; 
     289} 
     290 
     291use BADC::FTPaccess; 
     292 
     293sub getFTPAccessFileReadPermissionsInfo 
     294{ 
     295    # Adapted from FTPaccess::read_access for use with NDG Security - changed so that 
     296    # all access info is returned and now username or user group info is checked.  The 
     297    # latter needs to be done by python code checking the user's NDG Attribute Certificate 
     298     
     299    #  Returns flag indicating if the user is allowed to read the directory containing  
     300    # the given file. Also returns hash giving information about how the result was  
     301    # arrived at. 
     302    my $filePath = shift;   # File or directory name to check access for 
     303    my %info; 
     304             
     305    my $ftpaccess_file = BADC::FTPaccess::find_nearest_ftpaccess_file($filePath); 
     306    my $ftpaccess = BADC::FTPaccess->new($ftpaccess_file); 
     307     
     308    $info{filePath} = $ftpaccess_file; 
     309 
     310    # Check that we do actually have an ftpaccess file to interogate. If not then grant 
     311    # read access 
     312    if (not $ftpaccess)  
     313    { 
     314        $info{noobj} = 1;  
     315        $info{msg} = "no .ftpaccess files found"; 
     316        return (1, \%info); 
     317    } 
     318 
     319    #  Check for public access 
     320    if ($ftpaccess->publicAccess("read"))  
     321    { 
     322        $info{public} = 1; 
     323        $info{msg} = "file has public read permissions"; 
     324        return (1, \%info); 
     325    }  
     326 
     327    #  Check allowed groups 
     328    my @allowGroups = $ftpaccess->allowedGroups("read"); 
     329    $info{allowedGroups} = \@allowGroups; 
     330 
     331    #  Check any lines that contain multiple groups 
     332    my @requiredGroups = $ftpaccess->allowedMultiGroups("read"); 
     333    $info{requiredGroups} = \@requiredGroups; 
     334     
     335    #  Check if the user's username is explicitly granted access 
     336    my @allowedUsers = $ftpaccess->allowedUsers("read"); 
     337    $info{allowedUsers} = \@allowedUsers; 
     338     
     339    $info{msg} =  
     340        "username and/or group information is needed to determine access permissions"; 
     341    return (0, \%info); 
     342} 
    162343 
    163344use Inline Python => <<'END'; 
    164345import base64 
    165 def urlSafeB64Encode(str): 
     346def pyUrlSafeB64Encode(str): 
    166347    return base64.urlsafe_b64encode(str) 
     348    
     349import os 
     350from logging.config import fileConfig 
     351    
     352# TODO: Remove  and set from some other env var 
     353os.environ["NDGSEC_DIR"] = "/var/www/cgi-bin/NDG/Security"; 
     354try: 
     355    _logConfig = os.path.join(os.environ["NDGSEC_DIR"], 
     356                              'conf', 
     357                              'ndg-security-pep-log.cfg') 
     358    fileConfig(_logConfig) 
     359except KeyError: 
     360    from warnings import warn 
     361    warn(\ 
     362    '"NDGSEC_DIR" environment variable must be set to enable logging config', 
     363    RuntimeWarning) 
     364         
     365from ndg.security.common.authz.pep import PEP 
     366from ndg.security.common.authz.pdp import PDPError 
     367import logging 
     368log = logging.getLogger(__name__) 
     369 
     370def pyPEP(resrcHandle, userHandle, cfgFilePath): 
     371    """Wrapper to NDG Security Python Policy Enforcement Point""" 
     372     
     373    log.debug("resrcHandle = %s" % resrcHandle) 
     374    #userHandle = dict(h=sessionMgrURI, sid=userSessionID) 
     375    log.debug("userHandle = %s" % userHandle) 
     376 
     377    pep = PEP(cfgFilePath=cfgFilePath) 
     378         
     379    # dict is converted into a reference to a hash 
     380    try: 
     381        pep(resrcHandle, userHandle, None) 
     382    except PDPError, e:  
     383        return {'accessGranted': 0, 'msg': str(e)} 
     384     
     385    return {'accessGranted': 1, 'msg': "Access permitted"} 
    167386END 
    168387 
  • TI12-security/trunk/perl/ndglogin.pl

    r3775 r3780  
    88 
    99my $cgi = CGI->new(); 
    10 #my $session = ndgsecurity::ssoclient->new($cgi); 
    11 my $session = eval {new NDG::Security::Client($cgi)}; 
     10my $cfgFilePath = "/var/www/cgi-bin/NDG/Security/conf/ndg-security-client.cfg"; 
     11my $session = eval {new NDG::Security::Client($cfgFilePath, $cgi)}; 
    1212 
    1313# Call Single Sign On handler 
Note: See TracChangeset for help on using the changeset viewer.