Python Class structure to allow multiple authentication methods - python

I'm building an application and one of the packages manage multiple auth methods.
Now it supports LDAP and PAM but I want in the future it supports a few more.
I have a package with
PAM.py and
LDAP.py
for example PAM.py contents:
import pam
class pam_auth:
def __init__(self, username=None, password=None):
self.username=username
self.password=password
def login(self):
res_auth=pam.authenticate(username=self.username, password=password)
return res_auth
and in another package I have the next class Login:
class Login:
def __init__(self,method=None):
self.authmethod=method
def login(self):
res_login=self.authmethod.login()
return res_login
Now i'm building my auth code like:
p=pam_auth()
p.username="pep"
p.password="just"
l=Login(method=p)
print l.login
And I believe that it is not the best way to do it, and thinking in multiples and different methods to auth.
For Example may be something like?:
l=Login(method=PAM.pam_auth)
l.username="pep"
l.password="just"
print l.login()
¿What is that I must change in Login Class or PAM class to work in this way?

For the change you mentioned, all you need to do is to instanciate the class inside Login's __init__:
class Login:
def __init__(self,method):
self.authmethod=method()
However, as Stefano Sanfilippo mentioned, this may actually hamper modularity, since suddenly Login must know the constructor parameters of the authentication method.
A couple more tips:
If you're writing python 2, you'll want to create new-style classes:
instead of
class Login:
use
class Login(object):
Also, if you're writing a general authentication layer, you probably don't want to deal explicitly with usernames and passwords: what will happen when you want to use third-factor, smartcard or biometric authentication in the future? You probably should deal with opaque "data", that the authentication method receives, unaltered.

Related

How can I reference an object in a class method without passing it as an argument?

I'm writing a module to make requests to a work-related website easier. I have one class that initializes the session and has the URL as a variable. I wrote an abstract class to encapsulate the logic of the requests, and one subclass for each of the data-types. Ideally, most of the methods should be class methods, so I can call them by writing module.subclass1.get(params). My problem is that all of these methods need a session, and an URL to make the requests, and I can't figure out a good way to implement this. My code is looking something like this at the moment.
#This class I make an instance of, to have the session and url at hand.
class Website:
def __init__(self, username, password, url):
self.session = getSession(username, password)
self.url = url
#The abstract class that handles the requests
class AbstractClass(abc.ABC):
classvar1 = ""
classvar2 = ""
#classmethod
def get(cls, website, params):
website.session.get(website.url + cls.classvar1, params)
class Subclass1(AbstractClass):
classvar1 = "Something else"
classvar2 = "Something else"
Some of my (suboptimal) solutions are:
1. Passing an instance of Website as an argument to the class methods
This works and is what I do for now, but I would have to pass the website object as a parameter all the time.
All calls would end up looking like this:
module.subclass1.method(website, params)
2. Making the website object a class variable in the abstract class.
This works and makes the method calls look pretty: module.Subclass1.get(params) But it seems like very bad practice, and I would like to always have to specify a username, password, and URL, so different scripts can use different logins and so I can change the URL to test or prod environment.
3. Initializing all the subclasses with a website object in the Website class
This also makes the method calls look pretty website.Subclass1.method(params), but I would have to initialize every single subclass in the Website __init__ method, and would have to change the abstract class to add an__init__ method that takes a Website object as an argument.
Is there any smarter way to accomplish this? Any help is much appreciated.

in Django, can I use a class only to group functions?

I am creating an application using Django. The application has user registration and login functionality.
I have three functions related to user authentication as of now, login, registration, and email_check (which is called when the user types email address to see if it is available). I was trying to group these functions under a class for better organisation and easy accessibility.
So I wrote a class and function like so:
class user_auth:
def check_email(email):
with connection.cursor() as conn:
conn.execute('select count(*) from user_info where email = %s', [email])
row = conn.fetchall()
response = bool(row[0][0])
return(response)
However, when I do this, I get a pylint error saying Method should have "self" as the first argument.
If I save this and call it as user_auth.check_email('abc#xyz.com'), it works just fine. But if I add self as the first argument, it stops working.
Am I using classes in an incorrect way? If yes, what is a better way to create a group of functions like this which can be easily imported using a single statement in other files?
As in the comments said, you could use a module for it.
Otherwise if you want to group them inside a class you just need to use the staticmethod or classmethod decorator.
Example:
class user_auth:
#classmethod
def check_email(cls, email):
...
#staticmethod
def static_check_email(email):
...

What is the clean way to integrate another service to django

I have a Django 3 application, using an LDAP service class, like this :
class LDAPService:
def init(self, host: str, user: str, password: str, ssl: bool = True):
...
def bind(): # The connection is done here, __init__ just sets values
....
def create_ou(base: str, ou_name: str):
....
Where (or when) should I initialize the service to use it in views ? The bind step takes about 2 seconds to apply, I can not do it on every request. How can I keep an instance of this class shared, and not done every single time ? I may have a solution using singleton, and/or initializing it in like settings files, but i think there is a better way to do it.
I know in production, there may be multiple workers, so multiple instances, but i am ok with it.
Another question: How can everything above be done, with connections credentials from a database model (so not at django startup, but at anytime)
I am totally new to the django ecosystem, the things i have found about a service layer were all about django models. I want to do the same interface i would do for models in a regular service layer, but working on something else than django models.
I think the LDAP connection itself should not be there, only the CRUD methods, but i do not know where to place it, and how to make django interact with.
Thanks in advance for your suggestions :)
You can use a memoized factory function:
def get_ldap_service() -> LDAPService:
if not hasattr(get_ldap_service, 'instance'):
get_ldap_service.instance = LDAPService(**input_from_somewhere)
return get_ldap_service.instance
This is cleaner than a Singleton and allows for easier testability of the service class.
Additionally, it might be a better design to send the low-level connection logic to another class, say
class LDAPConnection:
def __init__(self, host: str, user: str, password: str, ssl: bool = True):
...
and then your service layer would take that as a dependency at run-time (dependency injection)
class LDAPService:
def __init__(self, connection: LDAPConnection):
self.connection = connection
# CRUD operations
def create_ou(self, base: str, ou_name: str):
# Do operations via self.connection
...
This allows for different connections exposing the same interface.
You can build from these two ideas (dependency injection and caching) to get more complicated general structures in a maintainable way.

Accessing functions of another class without referencing in every instance?

In the code below the User class needs to access a function get_user inside an instance of WebService class, as that contains other functions required for authentication with the web server (last.fm). Actual code is here.
class WebService:
def __init__(self, key):
self.apikey = key
def get_user(self, name):
pass # Omitted
class User:
def __init__(self, name, webservice):
self.name = name
self.ws = webservice
def fill_profile(self):
data = self.ws.GetUser(self.name)
# Omitted
The problem is that a reference needs to be held inside every ´User´. Is there another way of doing this? Or is it just me overcomplicating things, and this is how it actually works in the real world?
As requested:
As to handling things like get_top_albums and get_friends, that depends on how you want to model the system. If you don't want to cache the data locally, I'd say just call the service each time with a user ID. If you do want to cache the data locally, you could pass a User object to the method in WebService, then have the method populate the members of the User. You do have to make a design decision though to either have a WebService and a User (what would probably be best), or just a UserWebService.
You can certainly make the reference a static variable, if the web service object is the same for all users.
The syntax is:
class User:
webservice = ...
...
You will then even be able to access it from User instances, but not to assign to it that way, that would require User.webservice syntax.
You are also getting good design alternatives suggested in the comments.

How to mock users and requests in django

I have django code that interacts with request objects or user objects. For instance something like:
foo_model_instance = models.get_or_create_foo_from_user(request.user)
If you were going to test with the django python shell or in a unittest, what would you pass in there? Here simply a User object will do, but the need for a mock request object also comes up frequently.
For the shell or for unittests:
How do you mock users?
How do you mock requests?
For request, I would use RequestFactory included with Django.
from django.test.client import RequestFactory
rf = RequestFactory()
get_request = rf.get('/hello/')
post_request = rf.post('/submit/', {'foo': 'bar'})
for users, I would use django.contrib.auth.models.User as #ozan suggested and maybe with factory boy for speed (with factory boy you can choose to not to save to DB)
How do you mock users?
Initialise a django.contrib.auth.models.User object. User.objects.create_user makes this easy.
How do you mock requests?
Initialise a django.http.HttpRequest object.
Of course, there are shortcuts depending on what you want to do. If you just need an object with a user attribute that points to a user, simply create something (anything) and give it that attribute.
You can either roll your own mocks, as Anurag Uniyal has suggested, or you can use a mocking framework.
In response to those saying you can just create an ordinary user as you would anyway in Django... I would suggest this defeats the point of the unit test. A unit test shouldn't touch the database, but by creating a user, you've changed the database, hence why we would want to mock one.
You don't need to mock Users, as you can just create one within your test - the database is destroyed after the test is finished.
To mock requests, use this snippet from Simon Willison.
Read about mock objects here
http://en.wikipedia.org/wiki/Mock_object
http://www.mockobjects.com/
And use this python lib to mock a user
http://python-mock.sourceforge.net/
else you can write a simple User class yourself, use this as a starting point
class MockUser(object):
def __call__(self, *args, **kwargs):
return self
def __getattr__(Self, name):
return self
add specfic cases etc etc
There are already a lot of good general answers. Here is a simple mock user used in tests involving admin forms:
class MockUser:
is_active = True
is_staff = True
def has_perm(self, *args):
return True
from django.test.client import RequestFactory
request = RequestFactory().get("/some/url")
request.user = MockUser()

Categories