Can wagtail set specific pages to be edited only by specific owner? - wagtail

I'm try to use PagePermissionHelper to set owner permission, but it's just let button disappear, user still can play it with url.
and hooks 'construct_main_menu' can play it with url too.
class ValidationPermissionHelper(PagePermissionHelper):
def user_is_owner(self, user, obj):
if user == obj.owner:
return True
else:
return False
def user_can_edit_obj(self, user, obj):
return self.user_is_owner(user, obj)
class StorePage(ModelAdmin):
model = Store
permission_helper_class = ValidationPermissionHelper
Store is Page Model, so, PagePermissionHelper is the only choice?
how to really disable the edit permission?

Pages are Per-Tree permitted, I think if you change settings of groups, giving them the per-instance permissions, would be better than the your code, unless you have (more complex use case)..!

Related

How to show content to staff user only using React and DRF

I have a blog create view, where admin or staff user can only create blog, it is restricted from normal user and it is working fine.
Goal: Now I want to display the create blog button on the website to admin or staff user only if they logged in from their account.
I have user detail view that is calling on the very beginning of site loading, How can I check if the user is admin or staff user or normal user in response?
I am using React in frontend with redux.
What I have done so far
path('user/', UserDetailsView.as_view(), name='rest_user_details'),
class UserDetailsView(RetrieveUpdateAPIView):
serializer_class = UserDetailsSerializer
permission_classes = (IsAuthenticated,)
def get_object(self):
return self.request.user
def get_queryset(self):
return get_user_model().objects.none()
One possible solution is to overwrite your UserDetailsSerializer and added a field like is_admin_user.
We are going to use serializerMethodField from Django-Restframework serializers.
class UserDetailsSerializer(serializers.ModelSerializer):
is_admin_user = serializers.SerializerMethodField()
class Meta:
model = User
fields = ('is_admin_user',) // And all other necessary fields
def get_is_admin_user(self, obj):
return obj.is_staff # this will return true for self.is_staff user
Our main goal is to pass some information to frontend about user role, so that from this information we can decide whether to show create blog button or not.

how to test wagtail admin pages using the django test client?

I'm trying to write some tests using the Django Test Client to check on my customisations of the wagtail admin. I've tried:
self.user = get_user_model().objects.create(
username='addy', is_staff=True
)
self.client.force_login(self.user)
response = self.client.get(f'/admin/pages/{self.thing.id}/edit/')
But I still end up seeing an HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/admin/login/?next=/admin/pages/6/edit/">
Am I missing some crucial attribute to the user that Wagtail wants in ordet to let them view wagtail-admin pages?
Wagtail doesn't use the is_staff flag to determine access to the admin - you need to assign your user the wagtailadmin.access_admin permission instead.
See https://github.com/wagtail/wagtail/blob/c6666c6de5e83bf94d18324858c121e6584ba47d/wagtail/wagtailsites/tests.py#L258 for an example of setting up a test user with the right permissions.
Here's what worked for me in the end:
self.user = get_user_model().objects.create_superuser(
username='addy', email='admin#example.com', password='passwood321'
)
Just setting is_staff wasn't enough. With due thanks to #gasman above, users don't have an is_admin atttribute. they do have is_superuser, so this code works just as well (and is probably better since it doesn't need an irrelevant email or password):
self.user = get_user_model().objects.create(username='addy', is_superuser=True)
self.client.force_login(self.user)

Which is the best way to restrict access to certain pages in my website to certain users other than admin using cakephp

I have a website where all the pages are accessible to the public except for one Releases page which is user specific or maybe to a specific group .I have a seperate login page to gain access to 'Releases' page based on authentication.How do I go about this?Using Acl or Authorize function?I am very confused..Also do i need to use the same users table for authenticating this page, in that case do I use this User login page as an elemnt in my other login page.Could somebody please hint me on how to proceed?
ACL is overkill for many situations.
What I normally do is something like this in my controller:
public function releases() {
$this->_allowedGroups(array(1,2,3));
// rest of code here
}
Then in my app controller:
public function _allowedGroups($groups=array()) {
if( !in_array($this->Auth->user('group_id'), $groups) ) {
$this->redirect(array('controller'=>'users', 'action'=>'login'));
}
}
Acl should do your work.
And is there any specific need that you are using a separate login page??
A single login page and and a single users table should suffice your needs if you implement acl. Only those users who have rights to view the Requests page will be allowed to do so.
you may do something like this..
on core.php, put
Configure::write('Routing.prefixes', array('release'));
and do the verification on the AppController:
class AppController extends Controller{
public function beforeFilter(){
if (isset($this->params['prefix']) and $this->params['prefix'] == 'release'){
if ($this->Session->read("User.type") != 'admin'){
//redirect the user or throw an error...
}
}
}
}
so, youdomain.com/release/* will only be accesible by your administrators...
also, i don't see why you need two logins pages... you could just put a flag on your users table saying if the user is or not an admin... and on the login, set the User.type property on session.
if you don't need of complex permissions control, i think you don't need use ACL.

How to add acl per row?

I have problem with ACL. I read tutorial from here and here, and now I know how to add permission to some user/group to edit his profile, but then all users can edit each other profile. How I can set permission so user can edit just his own profile, not others, or can I somehow put this code in edit function:
function edit($id = null) {
if ($logedUserId != $id) {
// deny access
return;
}
// edit user
}
Assuming that you are using action-based access control (which it appears as though you are), then unless you have an action named after each profile (which would be completely wrong) you will have to do an additional check within the edit() method to ensure that the profile being edited belongs to the currently logged in user.
So you sort of answered your own question--correctly.

Django users and authentication from external source

I have a Django app that gets it's data completely from an external source (queried via HTTP). That is, I don't have the option for a local database. Session data is stored in the cache (on my development server I use a SQLite database, so that is no error source). I'm using bleeding edge Django 1.1svn.
Enter the problem: I want to use Django's own authentication system for the users.
It seems quite simple to write my own Authentication Backend, but always just under the condition that you have a local database where to save the users. Without database my main problem is persistence.
I tried it with the following (assume that datasource.get() is a function that returns some kind of dict):
class ModelBackend (object):
"""Login backend."""
def authenticate (self, username=None, password=None):
"""Check, if a given user/password combination is valid"""
data = datasource.get ('login', username, password)
if data and data['ok']:
return MyUser (username=username)
else:
raise TypeError
return None
def get_user (self, username):
"""get data about a specific user"""
try:
data = datasource.get ('userdata', username)
if data and data['ok']:
return data.user
except:
pass
return None
class MyUser (User):
"""Django user who isn't saved in DB"""
def save (self):
return None
But the intentionally missing save() method on MyUser seems to break the session storage of a login.
How should MyUser look like without a local database?
OK, it's much more complicated than I thought. First, start with http://docs.djangoproject.com/en/dev/howto/auth-remote-user/, but you'll need to extend it with your own backend and user.
from django.contrib.auth.backends import RemoteUserBackend
class MyRemoteUserBackend (RemoteUserBackend):
# Create a User object if not already in the database?
create_unknown_user = False
def get_user (self, user_id):
user = somehow_create_an_instance_of (MyUser, user_id)
return user
def authenticate (self, **credentials):
check_credentials ()
user = somehow_create_an_instance_of (MyUser, credentials)
return user
Then the user:
from django.contrib.auth.models import User
class MyUser (User):
def save (self):
"""saving to DB disabled"""
pass
objects = None # we cannot really use this w/o local DB
username = "" # and all the other properties likewise.
# They're defined as model.CharField or similar,
# and we can't allow that
def get_group_permissions (self):
"""If you don't make your own permissions module,
the default also will use the DB. Throw it away"""
return [] # likewise with the other permission defs
def get_and_delete_messages (self):
"""Messages are stored in the DB. Darn!"""
return []
Phew! Django really isn't designed for usage without a database...
Rather than overwriting the save method, you may also disconnect the signal that invokes it. This is what I do in some apps which have read-only access to the user database.
# models.py (for instance)
from django.contrib.auth.models import update_last_login, user_logged_in
user_logged_in.disconnect(update_last_login)
grepping the source showed, that the only place user.save() is actually called (except for user creation and password management code, which you don't need to use at all) is django.contrib.auth.login(), to update user.last_login value.
# TODO: It would be nice to support different login methods, like signed cookies.
user.last_login = datetime.datetime.now()
user.save()
If you don't want user data to rest in DB, try adding dummy save() method. If I'm right, it should work.
def save(self, *args, **kwargs):
pass
Of course, because you have no persistence at all, you should consider caching datasource.get results, otherwise in worst case you may end up querying data again and again on every single logged in user's hit.

Resources