App-MFILE-WWW

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

0.003  2014-08-12 16:57 CEST
- bring in Resource.pm from App-Dochazka-REST and start adapting it

0.004  2014-08-13 03:37 CEST
- bin/dochazka-www: bring in server startup script from App-Dochazka-REST
  and adapt to our needs
- config/WWW_Config.pm: add some config params
- WWW.pm: bring in initialization routine from App-Dochazka-REST, adapt it, and
  call it from dochazka-www
- Resource.pm: eliminate large chunks of App-Dochazka-REST code that we don't
  need here; add debug messages; think through how requests will be validated
  by writing comments
- looking good overall -- the server starts and displays placeholder
  credentials dialog

0.005  2014-08-13 08:36 CEST
- change distro sharedir from 'config/' to 'share/', move config/ into it
- make a new 'comp/' subdir under share/, for Mason components
- make a new 'auth.mc' top-level Mason component for the credentials dialog
- WWW.pm: instantiate Mason interpreter and export it as $Mason singleton;
  load our distro sharedir instead of App-Dochazka-REST's; replace 

Changes  view on Meta::CPAN

  server with LWP::UserAgent object in session data
- current state: login dialog displays and user can enter credentials; each
  time the form is submitted, the credentials are authenticated to the REST
  server and the REST server's response is displayed

0.018  2014-08-18 10:06 CEST
- fix bug "messages not getting loaded properly due to typo in name of
  WWW_Message_en.conf file"
- rename 'auth.mi' to more descriptive 'login_dialog.mi'
- WWW.pm: add some POD detailing the request-response cycle
- Resource.pm: eliminate resource_exists and integrate _validate_session into
  is_authorized; improve session recognition; clarify that all POST requests 
  are treated as form submits and handled in 'process_post', which will not
  be reached if session not authorized
- dhandler.mc: clarify what dhandler.mc's job is 
- 02auth.js: rename to login_dialog.js and refactor to work with new
  generalized JSON structure for form submits

0.019  2014-08-18 10:43 CEST
- fix bug: "exception in Resource.pm->is_authorized"
- login_dialog.js: display reassuring message before attempting to

Changes  view on Meta::CPAN

- 013simpleForm2.js: fix bug "form submit broken when clicking on submit
  button"

0.059  2014-08-29 17:20 CEST
- Resource.pm->_login_dialog: improve error displayed on login failure
- 007directAction.js: constructor gives dA objects a default start method
  if one is not provided in the argument object; add 'insertEmployee' dA
- 100login.js: refactor MFILE.loginDialogFormSubmit, improve error message
  displayed on login failure
- 110empProfile.js: refactor MFILE.directAction.newEmployee.start so it
  properly validates the nick before attempting the insert, which is
  implemented by calling the new 'insertEmployee' dA target

0.060  2014-08-29 22:36 CEST
- implement "Masquerade as different employee" feature
- js/: add 'masqEmployee' target; rename 003targets.js to 003prototypes.js
  after adding MFILE.empProfile prototype to it; minor cleanup
- 011simpleMenu2.pm: check MFILE.currentEmployee's heritage each time we enter
  a menu, and fix it if necessary (it loses its heritage on each page reload,
  but each page reload takes us to the main menu, where we restore the heritage)
- HTML.pm: give 'userbox' an id so we can refer to it

lib/App/MFILE/WWW.pm  view on Meta::CPAN


Initialization routine - run from C<bin/mfile-www>, the server startup script.
This routine loads configuration parameters from files in the distro and site
configuration directories, and sets up logging.

FIXME: This code could be moved into the startup script.

=cut

sub init {
    my %ARGS = validate( @_, { 
        mode => { type => SCALAR, optional => 1 }, # 'STANDALONE' or 'DDIST', defaults to 'STANDALONE'
        ddist_sharedir => { type => SCALAR, optional => 1 },
        sitedir => { type => SCALAR, optional => 1 },
        debug_mode => { type => SCALAR, optional => 1 },
    } );

    # * determine mode
    my $mode = $ARGS{'mode'} || 'STANDALONE';
    if ( $mode =~ m/^(standalone|ddist)$/i ) {
        if ( $mode =~ m/^ddist$/i ) {

lib/App/MFILE/WWW/Dispatch.pm  view on Meta::CPAN

    if ( ! $method or ! $path or ! $body ) {
        $log->crit( 'POST request received, but missing mandatory attribute(s) - ' .
                    'here is the entire request body: ' . Dumper( $ajax ) );
        return 0;
    }

    # POST is used only for login/logout ATM
    if ( $method =~ m/^LOGIN/i ) {
        $log->debug( "Incoming login/logout attempt" );
        if ( $path =~ m/^login/i ) {
            return $self->validate_user_credentials( $body );
        } else {
            return $self->_logout( $body );
        }
    }

    $log->crit( "Asked to perform an AJAX call, but feature is not implemented!" );
    return 0;
}


=head2 validate_user_credentials

Called from C<process_post> to process login requests (special AJAX requests)
originating from the JavaScript side (i.e. the login screen in
login-dialog.js, via login.js).

Returns a status object - OK means the login was successful; all other statuses
mean unsuccessful.

=cut

sub validate_user_credentials {
    my ( $self, $body ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::validate_user_credentials()" );

    my $r = $self->request;
    my $session = $r->{'env'}->{'psgix.session'};
    my $nick = $body->{'nam'};
    my $password = $body->{'pwd'};
    my $standalone = $meta->META_WWW_STANDALONE_MODE;

    $log->debug( "Employee $nick login attempt" );

    my ( $code, $message, $body_json );

lib/App/MFILE/WWW/Resource.pm  view on Meta::CPAN


    ( length $uri > $site->MFILE_URI_MAX_LENGTH )
        ? 1
        : 0;
}


=head2 is_authorized

Since all requests go through this function at a fairly early stage, we
leverage it to validate the session.

=cut

sub is_authorized {
    my ( $self ) = @_;

    $log->debug( "Entering " . __PACKAGE__ . "::is_authorized()" );

    my $r = $self->request;
    my $session = $self->session;

lib/App/MFILE/WWW/Resource.pm  view on Meta::CPAN

        $log->warn( "Bypassing login dialog! Using default credentials" );
        #
        # since the credentials are set in the configuration file, we only
        # need to check them once - the result of this check is placed in
        # META_LOGIN_BYPASS_STATE
        #
        if ( not defined $meta->META_LOGIN_BYPASS_STATE ) {
            $meta->set('META_LOGIN_BYPASS_STATE', 0);
            $session->{'ip_addr'} = $remote_addr;
            $session->{'last_seen'} = time;
            my $bypass_status = $self->validate_user_credentials( {
                'nam' => $site->MFILE_WWW_DEFAULT_LOGIN_CREDENTIALS->{'nam'},
                'pwd' => $site->MFILE_WWW_DEFAULT_LOGIN_CREDENTIALS->{'pwd'},
            } );
            if ( $bypass_status->level() eq 'OK' ) {
                $meta->set('META_LOGIN_BYPASS_STATE', 1);
            }
        }
        return $meta->META_LOGIN_BYPASS_STATE;
    }

lib/App/MFILE/WWW/Resource.pm  view on Meta::CPAN


=head2 _is_fresh

Takes a single argument, the PSGI session, which is assumed to contain a
C<last_seen> attribute containing the number of seconds since epoch when the
session was last seen.

=cut

sub _is_fresh {
    my ( $session ) = validate_pos( @_, { type => HASHREF } );

    return 0 unless my $last_seen = $session->{'last_seen'};

    return ( time - $last_seen > $site->MFILE_WWW_SESSION_EXPIRATION_TIME )
        ? 0
        : 1;
}


=head2 known_content_type

lib/App/MFILE/WWW/Resource.pm  view on Meta::CPAN

    $log->debug( "Entering " . __PACKAGE__ . "::main_html" );

    $cepriv = '' unless defined( $cepriv );
    $log->debug( "Entering " . __PACKAGE__ . "::main_html() with \$ce " .
                 Dumper($ce) . " and \$cepriv " . $cepriv );

    my $r = '<!DOCTYPE html>';
    $r .= '<html>';
    $r .= '<head>';
    $r .= '<meta charset="utf-8">';
#    $r .= '<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">';
#    $r .= '<meta http-equiv="Pragma" content="no-cache">';
#    $r .= '<meta http-equiv="Expires" content="0">';
    $r .= "<title>App::MFILE::WWW " . $meta->META_MFILE_APPVERSION . "</title>";
    $r .= '<link rel="stylesheet" type="text/css" href="/css/start.css" />';

    # Bring in RequireJS with testing == 0 (false)
    $r .= $self->_require_js(0, $ce, $cepriv);

    $r .= '</head>';
    $r .= '<body>';

lib/App/MFILE/WWW/Resource.pm  view on Meta::CPAN

sub test_html {
    my ( $self ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::test_html" );

    my $r = '';
    
    $r = '<!DOCTYPE html>';
    $r .= '<html>';
    $r .= '<head>';
    $r .= '<meta charset="utf-8">';
    $r .= '<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">';
    $r .= '<meta http-equiv="Pragma" content="no-cache">';
    $r .= '<meta http-equiv="Expires" content="0">';
    $r .= "<title>App::MFILE::WWW " . $meta->META_MFILE_APPVERSION . " (Unit testing)</title>";
    $r .= '<link rel="stylesheet" type="text/css" href="/css/qunit.css" />';

    # Bring in RequireJS with testing == 1 (true)
    $r .= $self->_require_js(1);

    $r .= '</head><body>';
    $r .= '<div id="myLoadProgress" class="loadProgress">Loading RequireJS...</div>';

lib/App/MFILE/WWW/Resource.pm  view on Meta::CPAN

    $r .= '</script>';
    return $r;
} 


=head2 login_status

Once the username and password are known (either from C<process_post> via the
login AJAX request generated by the login dialog, or from the site
configuration via the MFILE_WWW_BYPASS_LOGIN_DIALOG mechanism), the
C<validate_user_credentials> method is called. That method is implemented by
the derived application, so it can validate user credentials however it likes.
The C<validate_user_credentials> method is then expected to call this method -
C<login_status> - to generate a status object from the results of the user
credentials validation.

Now, C<App::MFILE::WWW> does expect the C<validate_user_credentials> method to
provide the results of user credentials validation in a peculiar format,
hinging on the argument C<$code>, where a value of 200 indicates successful
validation and any other value indicates a failure.

=cut

sub login_status {
    my ( $self, $code, $message, $body_json ) = @_;

    my $status;

lib/App/MFILE/WWW/Resource.pm  view on Meta::CPAN

    body => [BODY OF HTTP RESPONSE, IF ANY] 

=cut

sub rest_req {
    my $self = shift;

    # process arguments
    my $ua = $self->ua();
    die "Bad user agent object" unless ref( $ua ) eq 'LWP::UserAgent';
    my %ARGS = validate( @_, {
        server =>   { type => SCALAR,  default => 'http://localhost:5000' },
        method =>   { type => SCALAR,  default => 'GET', regex => qr/^(GET|POST|PUT|DELETE)$/ },
        nick =>     { type => SCALAR,  optional => 1 },
        password => { type => SCALAR,  default => '' },
        path =>     { type => SCALAR,  default => '/' },
        req_body => { type => HASHREF, optional => 1 },
    } );
    $ARGS{'path'} =~ s/^\/*/\//;

    my $r;

share/js/core/qunit-2.4.0.js  view on Meta::CPAN

  			try {
  				block.call(currentTest.testEnvironment);
  			} catch (e) {
  				actual = e;
  			}
  			currentTest.ignoreGlobalErrors = false;

  			if (actual) {
  				var expectedType = objectType(expected);

  				// We don't want to validate thrown error
  				if (!expected) {
  					result = true;
  					expected = null;

  					// Expected is a regexp
  				} else if (expectedType === "regexp") {
  					result = expected.test(errorString(actual));

  					// Expected is a constructor, maybe an Error constructor
  				} else if (expectedType === "function" && actual instanceof expected) {

share/js/core/qunit.js  view on Meta::CPAN

  			try {
  				block.call(currentTest.testEnvironment);
  			} catch (e) {
  				actual = e;
  			}
  			currentTest.ignoreGlobalErrors = false;

  			if (actual) {
  				var expectedType = objectType(expected);

  				// We don't want to validate thrown error
  				if (!expected) {
  					result = true;
  					expected = null;

  					// Expected is a regexp
  				} else if (expectedType === "regexp") {
  					result = expected.test(errorString(actual));

  					// Expected is a constructor, maybe an Error constructor
  				} else if (expectedType === "function" && actual instanceof expected) {



( run in 0.276 second using v1.01-cache-2.11-cpan-4d50c553e7e )