FAQ
Hi

My existing Catalyst application is being extended. I want to keep a
record of previous passwords used by a user to prevent them being re-used.

I have Model 'UsedPassword' to keep track of the previous 8 (say)
passwords as so-

package MyApp::Schema::UsedPassword;
use strict;
use base qw(DBIx::Class);

__PACKAGE__->load_components(qw(PK::Auto Core));
__PACKAGE__->table('used_password');
__PACKAGE__->add_columns(qw(id user password));
__PACKAGE__->set_primary_key('id');

So, if I want a method (create_limited) to create a new UsedPassword
object, that ensures no more that 8 (say) passwords are stored in the
database (against each User) where should it go?

Ideally (I think) I would like to do something like

$c->model('DBIC::UsedPassword')->create_limited({
user => $user->id,
password => $password,
});

but i can't see how to add it to MyApp::Schema::UsedPassword (since
$c->model('DBIC::UsedPassword') returns a ResultSet not a
MyApp::Schema::UsedPassword)

Any other suggestions where to put it (polite one's only please)?

Regards
Ian

Search Discussions

  • Ian Docherty at Sep 13, 2007 at 10:58 am
    I have found a thread on DBIx-class mailing list that throws some light
    on this using

    http://search.cpan.org/~ash/DBIx-Class-0.08006/lib/DBIx/Class/Schema.pm#load_namespaces

    This seems to solve the problem for putting such logic into the
    Model/Schema but would it be better to put this type of logic into a
    business logic layer? In which case how would I obtain a $schema object?
    Would I have to then pass this as a parameter to the method?

    Regards
    Ian


    Ian Docherty wrote:
    Hi

    My existing Catalyst application is being extended. I want to keep a
    record of previous passwords used by a user to prevent them being
    re-used.

    I have Model 'UsedPassword' to keep track of the previous 8 (say)
    passwords as so-

    package MyApp::Schema::UsedPassword;
    use strict;
    use base qw(DBIx::Class);

    __PACKAGE__->load_components(qw(PK::Auto Core));
    __PACKAGE__->table('used_password');
    __PACKAGE__->add_columns(qw(id user password));
    __PACKAGE__->set_primary_key('id');

    So, if I want a method (create_limited) to create a new UsedPassword
    object, that ensures no more that 8 (say) passwords are stored in the
    database (against each User) where should it go?

    Ideally (I think) I would like to do something like

    $c->model('DBIC::UsedPassword')->create_limited({
    user => $user->id,
    password => $password,
    });

    but i can't see how to add it to MyApp::Schema::UsedPassword (since
    $c->model('DBIC::UsedPassword') returns a ResultSet not a
    MyApp::Schema::UsedPassword)

    Any other suggestions where to put it (polite one's only please)?

    Regards
    Ian


    _______________________________________________
    List: Catalyst@lists.rawmode.org
    Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
    Searchable archive:
    http://www.mail-archive.com/catalyst@lists.rawmode.org/
    Dev site: http://dev.catalyst.perl.org/
  • Will Hawes at Sep 13, 2007 at 11:25 am

    On 13/09/2007, Ian Docherty wrote:
    I have found a thread on DBIx-class mailing list that throws some light
    on this using


    http://search.cpan.org/~ash/DBIx-Class-0.08006/lib/DBIx/Class/Schema.pm#load_namespaces

    This seems to solve the problem for putting such logic into the
    Model/Schema but would it be better to put this type of logic into a
    business logic layer? In which case how would I obtain a $schema object?
    Would I have to then pass this as a parameter to the method?

    Regards
    Ian


    Ian Docherty wrote:
    Hi

    My existing Catalyst application is being extended. I want to keep a
    record of previous passwords used by a user to prevent them being
    re-used.

    I have Model 'UsedPassword' to keep track of the previous 8 (say)
    passwords as so-

    package MyApp::Schema::UsedPassword;
    use strict;
    use base qw(DBIx::Class);

    __PACKAGE__->load_components(qw(PK::Auto Core));
    __PACKAGE__->table('used_password');
    __PACKAGE__->add_columns(qw(id user password));
    __PACKAGE__->set_primary_key('id');

    So, if I want a method (create_limited) to create a new UsedPassword
    object, that ensures no more that 8 (say) passwords are stored in the
    database (against each User) where should it go?

    Ideally (I think) I would like to do something like

    $c->model('DBIC::UsedPassword')->create_limited({
    user => $user->id,
    password => $password,
    });

    but i can't see how to add it to MyApp::Schema::UsedPassword (since
    $c->model('DBIC::UsedPassword') returns a ResultSet not a
    MyApp::Schema::UsedPassword)

    Any other suggestions where to put it (polite one's only please)?

    Regards
    Ian

    Isn't this just a case of adding a create_limited() method to your model
    class?

    package MyApp::Schema::UsedPassword;
    ...
    sub create_limited {
    my( $self, $user, $password ) = @_;
    # password checking logic here
    }

    In your controller:

    $c->model('DBIC::UsedPassword')->create_limited( ... );
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20070913/b07f8eeb/attachment.htm
  • Ian Docherty at Sep 13, 2007 at 12:21 pm
    Will Hawes wrote:
    ...
    Isn't this just a case of adding a create_limited() method to your
    model class?

    package MyApp::Schema::UsedPassword;
    ...
    sub create_limited {
    my( $self, $user, $password ) = @_;
    # password checking logic here
    }

    In your controller:

    $c->model('DBIC::UsedPassword')->create_limited( ... );
    Well, that was my first thought, but I get the following error if I try
    to do that...

    Can't locate object method "create_limited" via package
    "DBIx::Class::ResultSet

    Regards
    Ian
  • Ian Docherty at Sep 13, 2007 at 12:32 pm
    My application has (effectively, subject to some cut and paste) the
    following.

    ################

    package MyApp::Schema;

    use strict;
    use warning;

    use base qw(DBIx::Class::Schema);

    __PACKAGE__->load_classes(qw(
    UsedPassword
    ));
    1;

    ################

    package MyApp::Schema::UsedPassword;

    use strict;
    use warning;

    use base qw(DBIx::Class);

    __PACKAGE__->load_components(qw(PK::Auto Core));
    __PACKAGE__->table('used_password');
    __PACKAGE__->add_columns(qw(id user password));
    __PACKAGE__->set_primary_key('id');

    sub create_limited {
    my ($self, $user, $password) = @_;

    # password checking logic here
    }
    1;

    ################

    package MyApp::Model::DBIC;

    use strict;
    use warning;

    use base qw(Catalyst::Model::DBIC::Schema);

    __PACKAGE__->config(
    schema_class => 'MyApp::Schema',
    connect_info => [
    MyApp->config->{db},
    MyApp->config->{db_user},
    MyApp->config->{db_password},
    {AutoCommit => 1, quote_char => '`', name_sep => '.'},
    ]);
    1;

    ################

    As I mentioned, if I try to do a call to
    $c->model('DBIC::UsedPassword')->create_limited( ... ); I get the fatal
    error

    Can't locate object method "create_limited" via package
    "DBIx::Class::ResultSet

    Which is why I think this is not the approach, unless you know otherwise?

    Regards
    Ian
  • Will Hawes at Sep 13, 2007 at 1:57 pm

    On 13/09/2007, Ian Docherty wrote:
    My application has (effectively, subject to some cut and paste) the
    following.

    ################

    package MyApp::Schema;

    use strict;
    use warning;

    use base qw(DBIx::Class::Schema);

    __PACKAGE__->load_classes(qw(
    UsedPassword
    ));
    1;

    ################

    package MyApp::Schema::UsedPassword;

    use strict;
    use warning;

    use base qw(DBIx::Class);

    __PACKAGE__->load_components(qw(PK::Auto Core));
    __PACKAGE__->table('used_password');
    __PACKAGE__->add_columns(qw(id user password));
    __PACKAGE__->set_primary_key('id');

    sub create_limited {
    my ($self, $user, $password) = @_;

    # password checking logic here
    }
    1;

    ################

    package MyApp::Model::DBIC;

    use strict;
    use warning;

    use base qw(Catalyst::Model::DBIC::Schema);

    __PACKAGE__->config(
    schema_class => 'MyApp::Schema',
    connect_info => [
    MyApp->config->{db},
    MyApp->config->{db_user},
    MyApp->config->{db_password},
    {AutoCommit => 1, quote_char => '`', name_sep => '.'},
    ]);
    1;

    ################

    As I mentioned, if I try to do a call to
    $c->model('DBIC::UsedPassword')->create_limited( ... ); I get the fatal
    error

    Can't locate object method "create_limited" via package
    "DBIx::Class::ResultSet

    Which is why I think this is not the approach, unless you know otherwise?

    Whoops, my bad. $c->model() does indeed return a DBIx::Class::ResultSet, so
    you would need to retrieve/create an instance of your UsedPassword class
    from the resultset in order to call any methods on it:

    my $used_password = $c->model('DBIC::UsedPassword')->create( { user =>
    'user', password => 'password' } );
    $used_password->foo_method()

    Having said that, if I understand correctly what you are trying to do, you
    probably don't want a create_limited method at all. I think you need to
    override the new() method in your UsedPassword class and perform the checks
    there instead:

    package MyApp::Schema::UsedPassword;
    ...

    sub new {
    my ( $class, $attrs ) = @_;
    my $user = $attrs->{user};
    my $password = $attrs->{password};

    # password checking logic here

    my $new = $class->next::method($attrs);
    return $new;
    }

    Also (and this may have been a typo on your part, but just in case), please
    note it's "use warnings" not "use warning" to enable warnings in Perl.

    Hope the above is useful.
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20070913/2269669b/attachment.htm
  • Simon Wilcox at Sep 13, 2007 at 2:18 pm

    Will Hawes wrote:
    Whoops, my bad. $c->model() does indeed return a DBIx::Class::ResultSet,
    so you would need to retrieve/create an instance of your UsedPassword
    class from the resultset in order to call any methods on it:

    my $used_password = $c->model('DBIC::UsedPassword')->create( { user =>
    'user', password => 'password' } );
    $used_password->foo_method()
    IIRC, can't you get the original record source back from the resultset ?

    my $used_password =
    $c->model('DBIC::UsedPassword')->result_source->create_limited();

    S.
  • Matt S Trout at Sep 13, 2007 at 3:12 pm

    On Thu, Sep 13, 2007 at 10:58:08AM +0100, Ian Docherty wrote:
    I have found a thread on DBIx-class mailing list that throws some light
    on this using

    http://search.cpan.org/~ash/DBIx-Class-0.08006/lib/DBIx/Class/Schema.pm#load_namespaces

    This seems to solve the problem for putting such logic into the
    Model/Schema but would it be better to put this type of logic into a
    business logic layer? In which case how would I obtain a $schema object?
    Would I have to then pass this as a parameter to the method?
    The model -is- where your business logic lives.

    The real question is whether your ORM should be directly in the model or
    not, but that's a whole different thread.

    --
    Matt S Trout Need help with your Catalyst or DBIx::Class project?
    Technical Director Want a managed development or deployment platform?
    Shadowcat Systems Ltd. Contact mst (at) shadowcatsystems.co.uk for a quote
    http://chainsawblues.vox.com/ http://www.shadowcat.co.uk/
  • Matt S Trout at Sep 13, 2007 at 3:13 pm

    On Thu, Sep 13, 2007 at 10:27:04AM +0100, Ian Docherty wrote:
    Hi

    My existing Catalyst application is being extended. I want to keep a
    record of previous passwords used by a user to prevent them being re-used.

    I have Model 'UsedPassword' to keep track of the previous 8 (say)
    passwords as so-

    package MyApp::Schema::UsedPassword;
    use strict;
    use base qw(DBIx::Class);

    __PACKAGE__->load_components(qw(PK::Auto Core));
    __PACKAGE__->table('used_password');
    __PACKAGE__->add_columns(qw(id user password));
    __PACKAGE__->set_primary_key('id');

    So, if I want a method (create_limited) to create a new UsedPassword
    object, that ensures no more that 8 (say) passwords are stored in the
    database (against each User) where should it go?

    Ideally (I think) I would like to do something like

    $c->model('DBIC::UsedPassword')->create_limited({
    user => $user->id,
    password => $password,
    });

    but i can't see how to add it to MyApp::Schema::UsedPassword (since
    $c->model('DBIC::UsedPassword') returns a ResultSet not a
    MyApp::Schema::UsedPassword)

    Any other suggestions where to put it (polite one's only please)?
    ^^ BLAM!

    Why not just have it as

    $user->_add_to_used_passwords

    and then hook that into $user->insert + ->update ?

    Perhaps better still, why not just make this a trigger? (unless you're using
    MySQL, in which case s/make this a trigger/use a database/ :)

    --
    Matt S Trout Need help with your Catalyst or DBIx::Class project?
    Technical Director Want a managed development or deployment platform?
    Shadowcat Systems Ltd. Contact mst (at) shadowcatsystems.co.uk for a quote
    http://chainsawblues.vox.com/ http://www.shadowcat.co.uk/
  • Ian Docherty at Sep 13, 2007 at 3:14 pm

    Simon Wilcox wrote:
    Will Hawes wrote:
    Whoops, my bad. $c->model() does indeed return a
    DBIx::Class::ResultSet, so you would need to retrieve/create an
    instance of your UsedPassword class from the resultset in order to
    call any methods on it:

    my $used_password = $c->model('DBIC::UsedPassword')->create( { user
    => 'user', password => 'password' } );
    $used_password->foo_method()
    IIRC, can't you get the original record source back from the resultset ?

    my $used_password =
    $c->model('DBIC::UsedPassword')->result_source->create_limited();

    S.
    Almost, if I do my $used_password =
    $c->model('DBIC::UsedPassword')->result_class->create_limited();

    it works. So that should do for now, thanks Simon and Will for your help ;)

    Regards
    Ian
  • Matt S Trout at Sep 13, 2007 at 4:20 pm

    On Thu, Sep 13, 2007 at 03:14:58PM +0100, Ian Docherty wrote:
    Simon Wilcox wrote:
    Will Hawes wrote:
    Whoops, my bad. $c->model() does indeed return a
    DBIx::Class::ResultSet, so you would need to retrieve/create an
    instance of your UsedPassword class from the resultset in order to
    call any methods on it:

    my $used_password = $c->model('DBIC::UsedPassword')->create( { user
    => 'user', password => 'password' } );
    $used_password->foo_method()
    IIRC, can't you get the original record source back from the resultset ?

    my $used_password =
    $c->model('DBIC::UsedPassword')->result_source->create_limited();

    S.
    Almost, if I do my $used_password =
    $c->model('DBIC::UsedPassword')->result_class->create_limited();

    it works. So that should do for now, thanks Simon and Will for your help ;)
    No. Don't do that.

    Really don't.

    It's utterly broken.

    --
    Matt S Trout Need help with your Catalyst or DBIx::Class project?
    Technical Director Want a managed development or deployment platform?
    Shadowcat Systems Ltd. Contact mst (at) shadowcatsystems.co.uk for a quote
    http://chainsawblues.vox.com/ http://www.shadowcat.co.uk/
  • Jonathan Rockway at Sep 13, 2007 at 4:48 pm

    Ian Docherty wrote:
    As I mentioned, if I try to do a call to
    $c->model('DBIC::UsedPassword')->create_limited( ... ); I get the
    fatal error

    Can't locate object method "create_limited" via package
    "DBIx::Class::ResultSet

    Which is why I think this is not the approach, unless you know otherwise?
    I'm not sure I would structure the code exactly like you did. I think I
    would add a method to the User class like:

    package My::Schema::User;
    ...
    sub set_password { txn_do(update current password, update used
    passwords) }

    Then you can say something like:

    $c->model('DBIC::Users')->find(42)->set_password('foobarbaz');

    and have everything work.

    Finally, if you want your create_limited to work, try:

    package My::ResultSet::UsedPassword;
    use base 'DBIx::Class::ResultSet';
    sub create_limited { ... };

    package My::Schema::UsedPassword;
    __PACKAGE__->resultset_class('My::ResultSet::UsedPassword');

    Regards,
    Jonathan Rockway

    -------------- next part --------------
    A non-text attachment was scrubbed...
    Name: signature.asc
    Type: application/pgp-signature
    Size: 370 bytes
    Desc: OpenPGP digital signature
    Url : http://lists.scsys.co.uk/pipermail/catalyst/attachments/20070913/e31e7ca4/signature.pgp
  • Simon Wilcox at Sep 13, 2007 at 5:50 pm

    Matt S Trout wrote:
    On Thu, Sep 13, 2007 at 03:14:58PM +0100, Ian Docherty wrote:
    Almost, if I do my $used_password =
    $c->model('DBIC::UsedPassword')->result_class->create_limited();

    it works. So that should do for now, thanks Simon and Will for your help ;)
    No. Don't do that.

    Really don't.

    It's utterly broken.
    Thanks. I'm not as familiar with the interaction of DBIx::Class and
    Catalyst::Model::* as you are (obviously, I didn't write 'em :) but from
    here:

    http://search.cpan.org/~ash/DBIx-Class-0.08007/lib/DBIx/Class/ResultSet.pm#result_class

    I find:

    result_class

    Arguments: $result_class?
    Return Value: $result_class

    An accessor for the class to use when creating row objects.
    Defaults
    to result_source->result_class - which in most cases is the name of
    the "table" class.

    This seems to be the right behaviour to me. Can you summarise why it is
    not ?

    Yet over here:

    http://search.cpan.org/~blblack/Catalyst-Model-DBIC-Schema-0.20/lib/Catalyst/Model/DBIC/Schema.pm

    I find:

    class

    Shortcut for ->schema->class


    So would the original be better written as:

    $c->model('DBIC::UsedPassword')->class->create_limited(); # ?

    If it's not this then the docs are really confusing and I'd really
    appreciate it if you could provide the correct usage or approach for
    this situation.

    Many thanks,

    Simon.
  • Matt S Trout at Sep 13, 2007 at 7:11 pm

    On Thu, Sep 13, 2007 at 05:50:21PM +0100, Simon Wilcox wrote:
    Matt S Trout wrote:
    On Thu, Sep 13, 2007 at 03:14:58PM +0100, Ian Docherty wrote:
    Almost, if I do my $used_password =
    $c->model('DBIC::UsedPassword')->result_class->create_limited();

    it works. So that should do for now, thanks Simon and Will for your help
    ;)
    No. Don't do that.

    Really don't.

    It's utterly broken.
    Thanks. I'm not as familiar with the interaction of DBIx::Class and
    Catalyst::Model::* as you are (obviously, I didn't write 'em :) but from
    here:

    http://search.cpan.org/~ash/DBIx-Class-0.08007/lib/DBIx/Class/ResultSet.pm#result_class

    I find:

    result_class

    Arguments: $result_class?
    Return Value: $result_class

    An accessor for the class to use when creating row objects.
    Defaults
    to result_source->result_class - which in most cases is the name of
    the "table" class.

    This seems to be the right behaviour to me. Can you summarise why it is
    not ?
    No, result_class behaves perfectly.

    Trying to create a record via a class method is WRONG. This is not Class::DBI.
    Don't do it.

    Collection methods should go on resultset objects. Always.

    Class method stuff will easily break in all sorts of fun ways because DBIC
    isn't designed to work like that.
    So would the original be better written as:

    $c->model('DBIC::UsedPassword')->class->create_limited(); # ?

    If it's not this then the docs are really confusing and I'd really
    appreciate it if you could provide the correct usage or approach for
    this situation.
    The docs are correct.

    That's just completely the wrong place for the create_limited method.

    --
    Matt S Trout Need help with your Catalyst or DBIx::Class project?
    Technical Director Want a managed development or deployment platform?
    Shadowcat Systems Ltd. Contact mst (at) shadowcatsystems.co.uk for a quote
    http://chainsawblues.vox.com/ http://www.shadowcat.co.uk/
  • Ash Berlin at Sep 13, 2007 at 9:02 pm

    Simon Wilcox wrote:
    Matt S Trout wrote:
    On Thu, Sep 13, 2007 at 03:14:58PM +0100, Ian Docherty wrote:
    Almost, if I do my $used_password =
    $c->model('DBIC::UsedPassword')->result_class->create_limited();

    it works. So that should do for now, thanks Simon and Will for your help ;)
    No. Don't do that.

    Really don't.

    It's utterly broken.
    Thanks. I'm not as familiar with the interaction of DBIx::Class and
    Catalyst::Model::* as you are (obviously, I didn't write 'em :) but from
    here:

    http://search.cpan.org/~ash/DBIx-Class-0.08007/lib/DBIx/Class/ResultSet.pm#result_class

    I find:

    result_class

    Arguments: $result_class?
    Return Value: $result_class

    An accessor for the class to use when creating row objects.
    Defaults
    to result_source->result_class - which in most cases is the name of
    the "table" class.

    This seems to be the right behaviour to me. Can you summarise why it is
    not ?

    Yet over here:

    http://search.cpan.org/~blblack/Catalyst-Model-DBIC-Schema-0.20/lib/Catalyst/Model/DBIC/Schema.pm

    I find:

    class

    Shortcut for ->schema->class


    So would the original be better written as:

    $c->model('DBIC::UsedPassword')->class->create_limited(); # ?
    Wouldn't it just be nicer if you could do:

    $c->model('DBIC::UsedPassword')->create

    Create a new class that inherits from DBIx::Class::ResultSet. then do

    __PACKAGE__->resultset_class('...::ResultSet::UsedPassword');

    In your source class.

    Nicer, no?
  • Simon Wilcox at Sep 14, 2007 at 12:43 pm

    Matt S Trout wrote:

    The docs are correct.

    That's just completely the wrong place for the create_limited method.
    I'd better read them more thoroughly then :-)

    I'm coming at this as an ex-Class::DBI user who uses DBIx::Class every
    few months. Each time I come into contact with it the state of the art
    has moved on so much it feels like I'm re-learning everything. But that
    is most definitely my problem !

    I appreciate that RTFM is the right way to go but could you give me a
    clue as where the right place for the create_limited method would be ?

    Many thanks,

    Simon
  • Matt S Trout at Sep 14, 2007 at 1:35 pm

    On Fri, Sep 14, 2007 at 12:43:15PM +0100, Simon Wilcox wrote:
    Matt S Trout wrote:
    The docs are correct.

    That's just completely the wrong place for the create_limited method.
    I'd better read them more thoroughly then :-)

    I'm coming at this as an ex-Class::DBI user who uses DBIx::Class every
    few months. Each time I come into contact with it the state of the art
    has moved on so much it feels like I'm re-learning everything. But that
    is most definitely my problem !

    I appreciate that RTFM is the right way to go but could you give me a
    clue as where the right place for the create_limited method would be ?
    Rule of thumb: If it would have been a class method in CDBI code it probably
    wants to be a resultset method in DBIC.

    As I noted in my initial reply to the OP though, I'd probably override
    insert+update on my user object to call an _add_pw_to_used_list method or
    similar; whether that then calls a create_limited resultset method for
    usedpassword or handles the logic itself doesn't strike me as that important.

    --
    Matt S Trout Need help with your Catalyst or DBIx::Class project?
    Technical Director Want a managed development or deployment platform?
    Shadowcat Systems Ltd. Contact mst (at) shadowcatsystems.co.uk for a quote
    http://chainsawblues.vox.com/ http://www.shadowcat.co.uk/

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupcatalyst @
categoriescatalyst, perl
postedSep 13, '07 at 10:27a
activeSep 14, '07 at 1:35p
posts17
users6
websitecatalystframework.org
irc#catalyst

People

Translate

site design / logo © 2022 Grokbase