tags:

views:

6190

answers:

3

I have a Flash application that is hosted from within a Drupal page. Some parts of the Flash application should be available to all users, but some should only be available to a logged-in user. (The specific role doesn't matter, just that they are any authorized user of the site).

From within Flash, I can detect whether the user is logged on by screen-scraping the "?q=user" page, but this is very brittle. What is the "right" way to do this? I can install additional Modules if necessary, but they need to be compatible with Drupal 6, not 5.

Similarly, if there is no user currently logged in, how can I take a username and password that they provide to me and log them in (or determine that the password is bad)?

+2  A: 

The "right" solution

you probably want to look at something like the services module. basically, you want to expose user services to your flash app, so you can send requests (via XML-RPC or whatever implementation you choose) to get information like "what access does the current logged in user have", or "login this user with username x and password y" etc...

The do it yourself quickly solution

if you're familiar with module development, you can create your own module quickly to just get the functionality you need, without having to go through a "real" web services layer. here's a quick example, creating a module called "myservices":

<?php
// myservices.info
   name = My Services
   description = Expose basic services
   core = 6.x

<?php
// myservices.module

   function myservices_menu() {
       $items['myservices/user'] = array(
         'title' => 'Get auth',
         'page callback' => 'myservices_get_user',
         'access arguments' => array('access content'),
       );
       return $items;
   }

   function myservices_get_user() {
       global $user;
       if (in_array('authenticated user', $user->roles) ) {
           print 'yes';
       } else {
           print 'no';
       }
   }

put those two files in a directory "myservices" in your modules directory, activate it, and then go to http://yourdomain.com/myservices/user

if you're authenticated, it'll just return yes, and if not no, etc... which you can pick up in your flash app.

Owen
+1  A: 

I was able to do this using Drupal's "system" and "user" services:

  • install the Services Module
  • enable the "system" and "user" services
  • for the code below, disable API Keys. (Or leave them enabled and supply the extra arguments)
  • permission both authenticated and anonymous users to access services

As of this writing, the AMFPHP service is not yet ported to Drupal 6, so I used XMLRPC. To use XMLRPC from ActionScript, I used the helper library at as3-rpclib. This library was written for ActionScript2, so using it with Flex3 required me to patch it, as described on this page (scroll down the page and search for a comment on May 10, 2008, by "jameshyu").

Once all those prerequisites are taken care of, you can check the currently logged-in user (if any) and/or log in a user with code like the following.

Here's the module that actually implements the login:

package
{
    import com.ak33m.rpc.xmlrpc.XMLRPCObject;

    import flash.events.*;
    import flash.net.*;

    import mx.collections.ItemResponder;
    import mx.rpc.AsyncToken;
    import mx.rpc.events.FaultEvent;
    import mx.rpc.events.ResultEvent;


    public class DrupalLogin
    {
     private var _api:XMLRPCObject;

     public function DrupalLogin( url:String )
     {
      _api = new XMLRPCObject();
      _api.endpoint = url;
      _api.destination = "";
     }

     public function set LoginResult( fn:Function ):void {_handleLoginResult = fn; }
     public function set CheckResult( fn:Function ):void {_handleCheckResult = fn; }
     public function set LogoutResult( fn:Function ):void {_handleLogoutResult = fn; }
     public function get User():String { return _user; }
     public function set TraceResult( fn:Function ):void {_handleTrace = fn; }

     private var _handleLoginResult:Function;
     private var _handleCheckResult:Function;
     private var _handleLogoutResult:Function;
     private var _handleTrace:Function;

     private function onTrace( st:String ):void
     {
      _handleTrace(st);
     }

     private var _firstCheckDone:Boolean = false;
     private var _loggedIn:Boolean = false;


     // The logged-in user's ID (if any)
     private var _user:String = "";



     // *****************************************
     // doLogin
     // *****************************************

     // Function doLogin kicks off the process.
     public function doLogin( user:String, pwd:String ):void
     {
      onTrace( "******************* doLogin ********************" );

      if( !_firstCheckDone )
      {
       _handleLoginResult( false, "ALWAYS CALL doCheck() FIRST TO SEE IF YOU NEED TO LOG IN OR NOT" );
       return;
      }
      if( _loggedIn )
      {
       _handleLoginResult( true, "YOU ARE ALREADY LOGGED IN" );
       return;
      }

      var token:AsyncToken = _api.call( "user.login", _sid, user, pwd );
      var tresponder:ItemResponder = new ItemResponder(this.onLoginInfo,this.onLoginFault);
      token.addResponder(tresponder);
     }


     private function onLoginInfo (event:ResultEvent,token:Object = null):void
     {
      onTrace( "... got onLoginInfo" );

      _user = event.result.user.name;
      _loggedIn = true;
      _handleLoginResult( true, "logged in ok" );   
     }

     private function onLoginFault (event:FaultEvent, token:Object=null):void
     {
      onTrace( "   got onLoginFault" );

      _loggedIn = false;
      _handleLoginResult( false, "Fault: " + event.fault.faultString + " -- " + event.fault.faultCode);
     }


     // *****************************************
     // doLogout
     // *****************************************

     public function doLogout():void
     {
      onTrace( "******************* doLogout ********************" );

      if( !_firstCheckDone )
      {
       _handleLogoutResult( false, "ALWAYS CALL doCheck() FIRST TO SEE IF YOU ARE ABLE TO LOG OUT OR NOT" );
       return;
      }
      if( !_loggedIn )
      {
       _handleLogoutResult( true, "YOU ARE ALREADY LOGGED OUT" );
       return;
      }

      var token:AsyncToken = _api.call( "user.logout", _sid );
      var tresponder:ItemResponder = new ItemResponder(this.onLogoutInfo,this.onLogoutFault);
      token.addResponder(tresponder);
     }

     private function onLogoutInfo (event:ResultEvent,token:Object = null):void
     {
      onTrace( "got onLogoutInfo" );

      _loggedIn = false;
      _handleLogoutResult( true, "logged out ok" );   
     }

     private function onLogoutFault (event:FaultEvent, token:Object=null):void
     {
      onTrace( "got onLogoutFault" );

      _loggedIn = false;
      _handleLogoutResult( false, "Fault: " + event.fault.faultString + " -- " + event.fault.faultCode);
     }


     // *****************************************
     // doCheckLogin
     // *****************************************

     private var _sid:String;

     public function doCheckLogin():void
     {
      onTrace( "******************* doCheckLogin ********************" );

      var token:AsyncToken = _api.call( "system.connect" );
      var tresponder:ItemResponder = new ItemResponder(this.onCheckInfo,this.onCheckFault);
      token.addResponder(tresponder);

     }

     private function onCheckInfo (event:ResultEvent,token:Object = null):void
     {
      onTrace( "got onCheckInfo" );

      _user = event.result.user.name;
      _sid = event.result.user.sid;
      var roles:Object = event.result.user.roles;
      _loggedIn = false;
      for( var i:int=0; i<10; i++ )
      {
       var tmp:String = roles[i.toString()];
       if( tmp == "authenticated user" )
        _loggedIn = true;
      }

      trace( "user = " + _user + ", sid=" + _sid + ", loggedIn=" + _loggedIn );
      _firstCheckDone = true;

      _handleCheckResult( _loggedIn, _loggedIn?("Currently logged in as " + _user):"Not logged in yet" );
     }

     private function onCheckFault (event:FaultEvent, token:Object=null):void
     {
      onTrace( "got onCheckFault" );

      _handleCheckResult( false, "Fault: " + event.fault.faultString + " -- " + event.fault.faultCode);
     }

    }
}

and here's an example of using it:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script source="LoginExample.as" />
    <mx:Button id="btnGoodLogin" click="btnGoodLogin_onClick()"  label="Good Login" enabled="true" y="28"/>
    <mx:Button id="btnBadLogin" click="btnBadLogin_onClick()"  label="Bad Login" enabled="true" y="28" x="112"/>
    <mx:Button id="btnLogout" click="btnLogout_onClick()"  label="Logout" enabled="true" y="28" x="219"/>
    <mx:Button id="btnCheck" click="btnCheck_onClick()"  label="Check" enabled="true" y="28" x="325"/>
    <mx:Text id="txtResult"  y="58" width="263"/>
</mx:Application>





import flash.events.*;
import flash.net.*;

private var _login:DrupalLogin;

private function setup():void
{
    if( _login==null )
    {
     var url:String = "http://myserver/mydrupal?q=services/xmlrpc";

     _login = new DrupalLogin(url);
     _login.CheckResult = handleCheckResult;
     _login.LoginResult = handleLoginResult;
     _login.LogoutResult = handleLogoutResult;
     _login.TraceResult = handleTraceResult;
    }
}

private function btnGoodLogin_onClick():void
{
    setup();
    _login.doLogin( "goodname", "goodpwd" );
}

private function btnBadLogin_onClick():void
{
    setup();
    _login.doLogin( "badname", "badpwd" );
}

private function btnLogout_onClick():void
{
    setup();
    _login.doLogout();
}

private function btnCheck_onClick():void
{
    setup();
    _login.doCheckLogin();
}



private function showResult( result:String):void
{
    trace( "showResult: " + result );
    txtResult.text = result; 
}

private function handleTraceResult( text:String ):void
{
    trace( text );
}



private function handleCheckResult( loggedIn:Boolean, txt:String="" ):void
{
    if( txt != "" )
     txt = " (" + txt + ")";

    if( loggedIn )
     showResult( "ALREADY LOGGED IN AS " + _login.User + txt);
    else
     showResult( "NOT LOGGED IN YET" + txt );
}

private function handleLoginResult( loggedIn:Boolean, txt:String="" ):void
{
    if( txt != "" )
     txt = " (" + txt + ")";

    if( loggedIn )
     showResult( "LOGIN ATTEMPT SUCCEEDED" + txt);
    else
     showResult( "LOGIN ATTEMPT FAILED" + txt );
}

private function handleLogoutResult( loggedOut:Boolean, txt:String="" ):void
{
    if( txt != "" )
     txt = " (" + txt + ")";

    if( loggedOut )
     showResult( "LOGOUT ATTEMPT SUCCEEDED" + txt );
    else
     showResult( "LOGOUT ATTEMPT FAILED" + txt);
}
Eric
A: 

I know this is a long way out from when this post was originally made, but if you are still interested in this and never took the time to research it a lot, I figured out how to successfully process a login through a flash document. (One that is embedded or external for that matter.) It does not even require AMFPHP to be installed on the system. (Which is good because last I checked the services module which AMFPHP depends on does not yet have a recommended status.)

I set up a demo page here: AS3 Login Demo Page

You can login and logout, and it will detect if you are already logged in. It is still a work in progress, but most of the stuff you need is laid out for you.

If you want to see the documentation and want a copy of the source all of that is available here: www.andrewnprice.com

I hope that helps. I should be making an update to that demo sometime in the next week, so check back if you want some of the other features I mention adding in the post.