Wally Hartshorn has asked for the wisdom of the Perl Monks concerning the following question:

This is probably blindingly obvious to most people, but I'm mystified. I'm working on my first project to use Class::DBI, accessing a MySQL database.

This project will consist of two CGI programs -- the query program, which will be used by the general public to view information from the database; and the update program, which will be used by staff to enter information into the database.

The database access used by the query program presumably should be limited to "select" permissions, while the database access used by the update program will need additional permissions (select, insert, delete).

My problem is this: How do I use Class::DBI to provide both types of access?

Here is the class for public (read-only) access:

package MyDB; use base 'Class::DBI'; __PACKAGE__->set_db('Main', 'dbi:mysql:mydb', 'public', 'password'); 1;

Here is the class for staff (read/write) access:

package MyDBUpdate; use base 'Class::DBI'; __PACKAGE__->set_db('Main', 'dbi:mysql:mydb', 'staff', 'password'); 1;

Here is (part of) a class for one of the six tables:

package Person; use base 'MyDB'; __PACKAGE__->table('person'); __PACKAGE__->columns(All => qw/ first_name last_name street city state zip /); 1;

My fear is that, for each of my tables, I need to create a class like this:

package PersonUpdate; use base 'MyDBUpdate'; __PACKAGE__->table('person'); __PACKAGE__->columns(All => qw/ first_name last_name street city state zip /); 1;

This would mean duplicating the information for each table, which doesn't seem right. I'm hoping there is something I'm missing. Can someone show me what it is? (Or am I being too paranoid by using a read-only account for public access and a read-write account for staff access?) I did some poking around in Super Search, but didn't find the answer to this. (Or I found it and didn't realize that I had done so!)

While writing this question, I did one last poke around Google and might have found the answer. Perhaps what I need to do is delete MyDBUpdate and instead, for each table, create a class like this:

package PersonUpdate; use base 'Person'; __PACKAGE__->set_db('Main', 'dbi:mysql:mydb', 'staff', 'password'); 1;

Does that sound right? (Presumably the user ID and password would be pulled from a config file rather than hard-coded in a half-dozen different source files.)

Also, while we're on the subject of Class::DBI, can someone point me to some good tutorials? I've found a few to get me started, but I'd like some more. The ones I've found include:

Are there others that are recommended reading?

Wally Hartshorn

(Plug: Visit JavaJunkies, PerlMonks for Java)

Replies are listed 'Best First'.
Re: Different Database Permissions Using Class::DBI
by perrin (Chancellor) on Jul 02, 2003 at 15:57 UTC
    First of all, yes, I think you're being too paranoid. You don't let end users run arbitrary code against these classes, do you? So who cares what they allow? In a persistent environment where database connections are kept open, multiple database logins can be a real problem because they tie up more resources.

    As for how to do it, it's pretty simple. That whole base class setup is just a suggestion. You can either not have one at all and decide which database login to use in your class based on some config thing, or you could make Person a base class and then subclass it with MutablePerson and ImmutablePerson, moving the set_db call down to these subclasses.

      Uh, you typically restrict your DB to do certain things 'cause you don't want people doing those things. That's why a lot of companies don't allow anything but stored proc's from beign run. You can't run arbitrary sql.

      Some that do allow sql, don't usually allow deletes, since you don't want an arbitrary "delete from blah".

      It's not paranoia.. it's good security. Or do you not believe in firewalls too.

        You don't understand. We're not talking about running arbitrary SQL here, just executing pre-defined method calls on a class. Security is typically enforced either in these method calls (i.e. do you have permission to call this method) or at an even higher level in the web interface. A person who isn't allowed to delete something from the database would simply not be presented with a delete option in the web UI. This makes the extra work to use different database logins redundant, so most web apps do not use multiple database logins for security.
Re: Different Database Permissions Using Class::DBI
by simon.proctor (Vicar) on Jul 02, 2003 at 15:45 UTC
    Can you not restrict this based on a user role or permission within the database itself? You can then give the restricted user details to the user CGI app and the other account to the staff program.

    HTH

    SP
Re: Different Database Permissions Using Class::DBI
by bilfurd (Hermit) on Jul 02, 2003 at 17:47 UTC
    As far as tutorials go, you might want to shell out the money for Programming the Perl DBI. I found that it paid for itself quickly.

    To be fair, I've also found myself staring into space and muttering under my breath, so results may vary...

Re: Different Database Permissions Using Class::DBI
by cleverett (Friar) on Jul 02, 2003 at 22:04 UTC
    My problem is this: How do I use Class::DBI to provide both types of access?

    Try this:

    Subclass Class::DBI, adding 2 methods: one that knows how to find out who is using the program (using %ENV?) and one that knows how to generate login credentials as needed. Then use that to call setdb with the right username and password. You might also add triggers that would create an exception if a 'public' user tried to alter the DB.

    Then subclass this intermediate class for each table.

    Something like:

    package MyDB; use base 'Class::DBI'; __PACKAGE__->set_db('Main', 'dbi:mysql:mydb', &db_login_credentials); sub db_login_credentials { my $group = &who_is_this; if ($group eq 'staff') { return ('staff', 'password'); } elsif ($group eq 'public') { return ('public', 'password'); } else { die 'who is this person?'; } } sub who_is_this { # do whatever it takles to figure out if it's public or # staff, and return either 'staff' or 'public'; } if (&who_is_this eq 'public') { __PACKAGE__->add_trigger(before_create => sub { die 'not authorized to create'; }); __PACKAGE__->add_trigger(before_update => sub { die 'not authorized to update'; }); __PACKAGE__->add_trigger(before_delete => sub { die 'not authorized to delete'; }); }
    This is a common pattern with Class::DBI.
Re: Different Database Permissions Using Class::DBI
by aquarium (Curate) on Jul 02, 2003 at 21:54 UTC
    utterly simple:
    setup the "select" and "update" users on mysql with according permissions. the cgi for the selects uses the "select" created mysql user and the updating cgi uses the "update" user during DBI->Connect.
    It's wiser to have separate mysql logins like this as the select cgi would likely collect data from a form and you may end up with injected code to escalate their mysql priviledges. Then you can just concentrate on making the update cgi nice and tight in terms of security.