'AnonymousUser' object is not iterable - accessing to UserDetailView by anonymous and authenticateds users - django-urls

I have the following custom User model:
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(_('username'), max_length=30)
email = models.EmailField(unique=True, null=True)
slug = models.SlugField(max_length=100, blank=True)
In addition I have the UserProfile model related with their respective Manager() to perform some operations of follow users betwenn them:
class UserProfileManager(models.Manager):
use_for_related_fields = True
def is_following(self, user, followed_by_user):
user_profile, created = UserProfile.objects.get_or_create(user=user)
if created:
return False
if followed_by_user in user_profile.following.all():
return True
return False
class UserProfile(models.Model):
# user.profile
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile')
# user.profile.following -- users I follow
# user.profile.followed_by -- users that follow me -- reverse relationship
following = models.ManyToManyField(settings.AUTH_USER_MODEL,
blank=True, related_name='followed_by')
objects = UserProfileManager()
def __str__(self):
return str(self.user)
Then, I create the detail view of an User, which I want visualize when an user is logged and too where an user is anonymous.
I have the URL:
url(r"^(?P<slug>[\w.#+-]+)/$", views.UserDetailView.as_view(), name='detail'),
The UserDetailView is the following:
class UserDetailView(generic.DetailView):
template_name = 'accounts/user_detail.html'
queryset = User.objects.all()
def get_object(self):
return get_object_or_404(User, slug__iexact=self.kwargs.get("slug")
)
def get_context_data(self, **kwargs):
context = super(UserDetailView, self).get_context_data(**kwargs)
user = self.request.user
following = UserProfile.objects.is_following(self.request.user, self.get_object())
context['following'] = following
if user.is_authenticated():
context['userprofile'] = user.profile
return context
When I access to http://localhost:8000/accounts/profiles/<username>/ URL , when the user who perform the request is authenticated, ALL is O.K
[17/Apr/2018 00:01:07] "GET /accounts/profiles/bgarcial/ HTTP/1.1" 200 41394
But, I want that this view to be acceded from anonymous users or un-authenticated users, which does not register in my application, then, when an anonymous user access to http://localhost:8000/accounts/profiles/<username>/ URL I get this message:
TypeError: 'AnonymousUser' object is not iterable
[17/Apr/2018 00:01:57] "GET /accounts/profiles/bgarcial/ HTTP/1.1" 500 147208
The inconvenient is in the custom manager method ( is_following() ) created in the UserProfileManager class and which are called in the UserDetailView
File "/home/bgarcial/workspace/hostayni_platform/accounts/views.py", line 89, in get_context_data
following = UserProfile.objects.is_following(self.request.user, self.get_object())
File "/home/bgarcial/workspace/hostayni_platform/accounts/models.py", line 572, in is_following
user_profile, created = UserProfile.objects.get_or_create(user=user)
File "/home/bgarcial/.virtualenvs/hostayni/lib/python3.6/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/bgarcial/.virtualenvs/hostayni/lib/python3.6/site-packages/django/db/models/query.py", line 473, in get_or_create
I try manage the request when this came from an anonymous user using the dispatch function inside my UserDetailView cbv:
def dispatch(self, request, slug, *args, **kwargs):
if request.user.is_anonymous():
username = User.objects.filter(slug__iexact=slug)
return HttpResponseRedirect(reverse_lazy("accounts:detail", slug=username))
else:
return super(UserDetailView, self).dispatch(request, slug, *args, **kwargs)
But I get this error message
TypeError: reverse() got an unexpected keyword argument 'slug'
[17/Apr/2018 00:07:43] "GET /accounts/profiles/bgarcial/ HTTP/1.1" 500 97476
return self.dispatch(request, *args, **kwargs)
File "/home/bgarcial/workspace/hostayni_platform/accounts/views.py", line 68, in dispatch
return HttpResponseRedirect(reverse_lazy("accounts:detail", slug=username))
File "/home/bgarcial/.virtualenvs/hostayni/lib/python3.6/site-packages/django/http/response.py", line 417, in __init__
I change the dispatch method by get method to get the anonymous request:
def get(self, request, slug, *args, **kwargs):
if request.user.is_anonymous():
username = User.objects.filter(slug__iexact=slug)
return redirect("accounts:detail", slug=username)
An I get NoReverseMatch message:
django.urls.exceptions.NoReverseMatch: Reverse for 'detail' with arguments '()' and keyword arguments '{'slug': <QuerySet [<User: #bgarcial>]>}' not found. 1 pattern(s) tried: ['accounts/profiles/(?P<slug>[\\w.#+-]+)/$']
the url requested does not match with the pattern that expected, the slug username field ...
I don't have clear when I should use get or dispatch method with the order to catch the anonymous request user and manage it.
Or can I manage this anonymous request user in the get_context_data method?
Any orientation that points me in the right direction will be highly appreciated
Best Regards

Related

Fastapi auth with OAuth2PasswordBearer, how to check if an user is connected without raise an exception?

For an application, I have followed the fastAPI documentation for the authentification process.
By default, OAuth2PasswordBearer raise an HTTPException with status code 401. So, I can't check if an user is actually connected without return a 401 error to the client.
An example of what I want to do:
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/users/token")
def get_current_user(token: str = Depends(oauth2_scheme)):
try:
settings = get_settings()
payload = jwt.decode(token, settings.secret_key,
algorithms=[settings.algorithm_hash])
email = payload.get("email")
if email is None:
raise credentials_exception
token_data = TokenData(email=email)
except jwt.JWTError:
raise credentials_exception
user = UserNode.get_node_with_email(token_data.email)
if user is None:
raise credentials_exception
return user
#app.get('/')
def is_connected(user = Depends(get_current_user)
# here, I can't do anything if the user is not connected,
# because an exception is raised in the OAuth2PasswordBearer __call__ method ...
return
I see OAuth2PasswordBearer class have an "auto_error" attribute, which controls if the function returns None or raises an error:
if not authorization or scheme.lower() != "bearer":
if self.auto_error:
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
)
else:
return None
So i think about a workaround:
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/users/token", auto_error=False)
def get_current_user(token: str = Depends(oauth2_scheme)):
if not token:
return None
# [ ... same token decoding logic than before ... ]
return user
#app.get('/')
def is_connected(user = Depends(get_current_user)
return user
It works, but I wonder what other ways there are to do this, is there a more "official" method?
This is a good question and as far as I know, there isn't an "official" answer that is universally agreed upon.
The approach I've seen most often in the FastAPI applications that I've reviewed involves creating multiple dependencies for each use case.
While the code works similarly to the example you've provided, the key difference is that it attempts to parse the JWT every time - and doesn't only raise the credentials exception when it does not exist. Make sure the dependency accounts for malformed JWTs, invalid JWTs, etc.
Here's an example adapted to the general structure you've specified:
# ...other code
oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="api/users/token",
auto_error=False
)
auth_service = AuthService() # service responsible for JWT management
async def get_user_from_token(
token: str = Depends(oauth2_scheme),
user_node: UserNode = Depends(get_user_node),
) -> Optional[User]:
try:
email = auth_service.get_email_from_token(
token=token,
secret_key=config.SECRET_KEY
)
user = await user_node.get_node_with_email(email)
return user
except Exception:
# exceptions may include no token, expired JWT, malformed JWT,
# or database errors - either way we ignore them and return None
return None
def get_current_user_required(
user: Optional[User] = Depends(get_user_from_token)
) -> Optional[User]:
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="An authenticated user is required for that action.",
headers={"WWW-Authenticate": "Bearer"},
)
return user
def get_current_user_optional(
user: Optional[User] = Depends(get_user_from_token)
) -> Optional[User]:
return user

Airflow: How to implement Dynamic html_content

I need to implement the html_content dynamic for custom email operator, as we have html_content different for different jobs.
Also, I need the values, for example, rows and filename be dynamic
The example below is one of the email body:
The `filename` has been delivered. `0 rows` for contact from 2020-06-14. If you have any questions or concerns regarding this feed please reply to this email
NOTE: The information contained in this email message is considered confidential and proprietary to the sender and is intended solely for review and use by the named recipient. Any unauthorized review, use, or distribution is strictly prohibited. If you have received this message in error, please advise the sender by reply email and delete the message.
Code:
def execute(self, context):
if self.source_task_ids:
ti = context['task_instance']
self.s3_key = ti.xcom_pull(task_ids=self.source_task_ids, key='s3_key')[0]
self.s3_key = self.get_s3_key(self.s3_key)
s3_hook = S3Hook(self.s3_conn_id)
try:
if not s3_hook.check_for_key(self.s3_key, bucket_name=self.s3_bucket):
logger.info(f'The source key {self.s3_key} does not exist in the {self.s3_bucket}')
rowcount = 0
self.subject = self.subject
self.html_content = self.html_content
else:
filedata = s3_hook.read_key(self.s3_key, bucket_name=self.s3_bucket)
rowcount = filedata.count('\n') - 1
logger.info(f'rowcount: {rowcount}')
self.subject = self.subject
self.html_content = self.html_content
self.snd_mail(self.send_from,self.send_to,self.subject, self.html_content, self.eml_server, files=self.files)
except Exception as e:
raise AirflowException(f'Error in sending the Email - {e}')
Airflow support Jinja templating in operators. It is build into the BaseOperator and controlled by the template_fields and template_ext fields of the base operator, e.g.:
class CustomEmailOperator(BaseOperator):
template_fields = ("html_content")
template_ext = (".html",)
#apply_defaults
def __init__(self, html_content, ...):
super().__init__(*args, **kwargs)
self.html_content = html_content
def execute(self, context):
# Rest of operator code, nothing special needs to happen to render the templates
Now the html_content field can either be a path to a jinja templated file with the .html extension or a html string directly. Parameters can be passed to the Jinja template using the params field of the operator:
task1 = CustomEmailOperator(
task_id = "task1",
html_content = "Hello, {{ params.name }}",
params = {
"name": "John",
},
...
)
That is how you could pass the filename and # of rows parameters. If you do not want to rely on the BaseOperator mechanism to template your email content, e.g. because you need a bit more control you can also use a helper function available in Airflow:
from airflow.utils.helpers import parse_template_string
html_content = "Hello, {{ params.name }}"
_, template = parse_template_string(html_content)
body = template.render({"name": "John"})

Django JWT authentication - user is anonymous in middleware

I am using Django JWT to power up authentication system in my project.
Also, I have a middleware, and the problem is that inside it, the user is anonymous for some reason, while in the view I am able to access the correct user by request.user. This issue is driving me crazy because some time ago this code worked perfectly ! Is this JWT's bug or I am doing something wrong ?
class TimezoneMiddleware(MiddlewareMixin):
def process_request(self, request):
# request.user is ANONYMOUS HERE !!!!
if not request.user.is_anonymous:
tzname = UserProfile.objects.get(user = request.user).tz_name
if tzname:
timezone.activate(pytz.timezone(tzname))
Relevant settings.py module:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_RENDERER_CLASSES': (
'djangorestframework_camel_case.render.CamelCaseJSONRenderer',
# Any other renders
),
'DEFAULT_PARSER_CLASSES': (
'djangorestframework_camel_case.parser.CamelCaseJSONParser',
# Any other parsers
),
}
JWT_AUTH = {
'JWT_ENCODE_HANDLER':
'rest_framework_jwt.utils.jwt_encode_handler',
'JWT_DECODE_HANDLER':
'rest_framework_jwt.utils.jwt_decode_handler',
'JWT_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_payload_handler',
'JWT_PAYLOAD_GET_USER_ID_HANDLER':
'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',
'JWT_RESPONSE_PAYLOAD_HANDLER': 'rest_framework_jwt.utils.jwt_response_payload_handler',
# 'rest_authentication.views.jwt_response_payload_handler',
'JWT_SECRET_KEY': SECRET_KEY,
'JWT_PUBLIC_KEY': None,
'JWT_PRIVATE_KEY': None,
'JWT_ALGORITHM': 'HS256',
'JWT_VERIFY': True,
'JWT_VERIFY_EXPIRATION': False,
'JWT_LEEWAY': 0,
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
'JWT_AUDIENCE': None,
'JWT_ISSUER': None,
'JWT_ALLOW_REFRESH': False,
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
'JWT_AUTH_HEADER_PREFIX': 'JWT',
}
I have also come across resources which helped me to retrieve the actual user, BUT ! I am still unable to set the timezone (timezone.activate(pytz.timezone(tzname)) seems to be ignored.
Yes, this issue is due to the JWT. You can checkout the discussion for it https://github.com/GetBlimp/django-rest-framework-jwt/issues/45 To fix this you will have to create a custom middleware which will set the request.user. Here is one I am using in my code:
from django.contrib.auth.middleware import get_user
from django.utils.functional import SimpleLazyObject
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class JWTAuthenticationMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.user = SimpleLazyObject(lambda:self.__class__.get_jwt_user(request))
return self.get_response(request)
#staticmethod
def get_jwt_user(request):
user = get_user(request)
if user.is_authenticated:
return user
jwt_authentication = JSONWebTokenAuthentication()
if jwt_authentication.get_jwt_value(request):
user, jwt = jwt_authentication.authenticate(request)
return user
Include this in the middlewares. It should come above all the middlewares which are using request.user.
#Atul Mishra: Thank you! Changed your version to the newest drf-jwt package (1.17.2). Seems like the the current github repository moved from this to here
from django.contrib.auth.middleware import get_user
from django.utils.functional import SimpleLazyObject
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class JWTAuthenticationInMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.user = SimpleLazyObject(lambda:self.__class__.get_jwt_user(request))
return self.get_response(request)
#staticmethod
def get_jwt_user(request):
# Already authenticated
user = get_user(request)
if user.is_authenticated:
return user
# Do JTW authentication
jwt_authentication = JSONWebTokenAuthentication()
authenticated = jwt_authentication.authenticate(request)
if authenticated:
user, jwt = authenticated
return user

PUT requests with DjangoRestFramework

I am building a rest API and am wondering both about HTTP best practices I guess, and how that would apply to the DRF. When sending a PUT request, in the body, do requests have all of the parameters for objects that they would be manipulating? Even if not all of them are changing? Or do they only send the fields that are being updated? So for example if we had a House object with No. of rooms and floors, and I was changing No. of rooms should I only send that parameter, or both of them?
If requests should only contain the fields that are being updating, then how would that translate to the DjangoRestFramework? Any help would be greatly appreciated!
My Views are:
class HouseDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = House.objects.all()
serializer_class = HouseSerializer
and serializer is:
class HouseSerializer(serializers.ModelSerializer):
quotes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = House
fields = (
'pk',
'address',
'quotes',
)
PUT is for full resource updates, PATCH is for partial resource updates because Fielding says so.
Therefore, in your example, if you wanted to only update a No. of rooms for your HouseDetail model, you would send the following payload in a PATCH request:
{ "no. of rooms": "42" }
It is still possible to partially update things with a PUT request in DRF (explained below) but you should simply use PATCH for that because it was created for this. You get PUT and PATCH functionality for your model because you have subclassed the generics.RetrieveUpdateDestroyAPIView class and have defined a serializer.
If a required field is omitted in your PUT request (see documentation for required), then you will receive a 400 status code with a response like {"detail": "x field is required"}. However, when you request via PATCH, there is a partial=True argument passed to the serializer which allows this partial only update. We can see this for the UpdateModelMixin:
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
which calls update, which is:
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)

Why do checkPermission and has_permission return different results?

Why do:
user.has_permission(permission, object)
and
user.checkPermission(permission, object)
return different results?
checkPermission seems to be the correct result.
Because they are different functions.
has_permission is a method of the BasicUser class in AccessControl/users.py:
def has_permission(self, permission, object):
"""Check if the user has a permission on an object.
This method is just for inspecting permission settings. For access
control use getSecurityManager().checkPermission() instead.
"""
roles=rolesForPermissionOn(permission, object)
if isinstance(roles, str):
roles=[roles]
return self.allowed(object, roles)
while checkPermission is a function defined in AccessControl/security.py:
def checkPermission(permission, object, interaction=None):
"""Return whether security policy allows permission on object.
Arguments:
permission -- A permission name
object -- The object being accessed according to the permission
interaction -- This zope.security concept has no equivalent in Zope 2,
and is ignored.
checkPermission is guaranteed to return True if permission is
CheckerPublic or None.
"""
if (permission in ('zope.Public', 'zope2.Public') or
permission is None or permission is CheckerPublic):
return True
if isinstance(permission, basestring):
permission = queryUtility(IPermission, unicode(permission))
if permission is None:
return False
if getSecurityManager().checkPermission(permission.title, object):
return True
return False
has_permission is used for inspecting permission settings while checkPermission is used for access control. In other words, a user may not have permission settings on an object but still have access via some other security policy mechanism.

Resources