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

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)

Related

How to get user information from database when the only thing in client browser is the AuthToken?

Building my first app with a real backend.
In my app, when a user registers or logs in (with username and password), a token is saved to the cookies of their browser. After registration (or when logging in), I can easily return information pertaining to this particular user (name, id, etc.).
# Django REST backend for loggin in and getting user token
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(
data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_id': user.pk,
'email': user.email,
'user_type': user.user_type,
})
Next time the user accesses the app in the same device, the token will be there and I can make the http requests for which a token is necessary. However, since I won't be logging the user in again (not asking for username and password every single session), I won't get that user's additional information.
In my React app I would like to have the user set in state at all times, e.g. user = {first_name: 'john', user_type: 'p'} but I don't know how to get the user info when the only thing I have is their token.
I am more than welcome to criticism to this approach and to learning what's the best way of doing this. I don't even know if keeping the user in state is the right way to do things...
I tried this:
class UserAPI(generics.RetrieveUpdateAPIView):
serializer_class = UserSerializer
def get_object(self):
print(self.request.user)
return self.request.user
curl -H "Authorization: Token b2e33463esdf8as7d9f8j34lf98sd8a" http://localhost:8000/current-user/
but the return value from self.request.user is AnonymousUser
If it's not sensitive information, such as username, id, user type, first name, etc. you can store this in localStorage.
problem was in my settings.py file:
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
There are two parts of answer to this question. first part is criticism to your approach and little bit of guidance towards better approach, second part of the answer is about your actual question.
Lets start with second part first.
From the look of your code, you are already storing the key in Token Table along with User. You can easily get the user by first running this query token = Token.objects.get(key=key).select_related('user') and then simple user = token.user will give you token.
Coming to first part now.
Since you are using DRF, you do not need to override the class unless extremely necessary. If you want user with each approved request, what you can do is simply add your Token verification class to Settings of DRF
I had the same problem but I found how to do that this code will help you
if request.user.is_authenticated:
user = Account.objects.filter(username=request.user)
serializer = UserSerializer(user, many=True)
return Response(serializer.data)
else:
return Response('You have to login first')

Cannot get Username / given_name when using angular-oauth2-oidc and Identity Server 4

I am following the Implicit Workflow example from the angular-oauth2-oidc documentation.
Everything works well in my Angular app, and I can login (during which I am redirected to Identity Server), get my token and use this token to access my Web Api.
However, I have noticed that the "given_name" claim is null, and therefore, the username is not displayed on the login page. Specifically, the following method from the sample code appears to return null:
public get name() {
let claims = this.oauthService.getIdentityClaims();
if (!claims) return null;
return claims.given_name;
}
I thought perhaps this was a problem with permissions, but my scope is set to:
scope: 'openid profile email api1',
Any idea what I need to change to get this "given_name" claim?
For those who encountered the same issue. You can fix it by adding this line AlwaysIncludeuserClaimsInIdToken=true in the client settings on identity provider side.
OauthService.getIdentityClaims() is a Promise and holds UserInfo you can extract the name field with braces, so your function should be:
public get name() {
let claims = this.oauthService.getIdentityClaims();
if (!claims) return null;
return claims['name'];
}
The answer marked as "Best answer" is not correct. Get the user claims in the 'idtoken' will cause that the 'idtoken' be very big and then you may exceed the size limit.
The correct implementation is to use the 'UserInfo' Endpoint and then use the method 'loadUserProfile':
Example:
getUserClaims() {
const user = this.oauthService.loadUserProfile();
console.log(user, user);
}
I had the same issue, in my case with an error displayed on the browser console, saying that Request was blocked by Security Policy.
even having the AllowAnyOrigin() method called in startup, I lacked to get the header allowed. So when in my angular aap i call the loadUserProfile method via the
token_received event, it sends some headers that were not allowed.
Finaly this fix my issue:
app.UseCors(options => options.AllowAnyOrigin().AllowAnyHeader());
Don't forget calling that before usemvc

How to set cookie in a CakePHP dispatcher filter?

In my application I need do some kind of "auto login" logic at the beginning of app work. In this "auto login" function I do many actions, and one of them - setting cookie, using CookieComponent.
When I use this autoLogin in controller or component - all is fine, BUT cookies are NOT set when I do the same from dispatcher filter.
I dig deep into CakePHP code, and found that when I try set cookie from dispatcher filter, $_cookies property of CakeResponse are empty. So it's looks like dispatcher filter creates own CakeResponse, and it resets later, so cookie are not set.
My filter looks like this:
class UserLoadFilter extends DispatcherFilter
{
public $priority = 8;
public function beforeDispatch($event) {
App::uses('AuthComponent', 'Controller/Component');
if (!AuthComponent::user()) {
App::uses('CustomAuthComponent', 'Controller/Component');
//$controller = new AppController($event->data['request'], $event->data['response']);
$auth = new CustomAuthComponent(new ComponentCollection(), Configure::read('Auth'));
$auth->autoLogin();
}
}
}
I also tried set cookie directly in beforeDispatch method in this way:
App::uses('CookieComponent', 'Controller/Component');
$cookie = new CookieComponent(new ComponentCollection());
$cookie->write('test', 'TEST!', false, "1 day");
but this has no sense too.
What do I do wrong? Maybe I just don't see some simple things, but I spent many time and still can't fix this. Is it's possible at all to set cookie from this place?
Sure I can try just use setcookie, or write own cookie wraper, but I want to do it in CakePHP style, using cookie component.
This looks just wrong to me. 2.x uses authentication and authorization objects so no CustomAuthComponent - meaning the component part - is needed. Instead you create customized authentication and authorization objects.
I see no reason why this has to be done in beforeDispatcher(). So what exactly is your goal? What kind of auth are you trying to implement?
Edit based on your comment:
Simply redirect after you identified the user as Boris said and your locale gets set (if you did it right). So just read the uuid from the cookie in beforeFilter(), get the user record based on that and use the user record to do a "manual" login and then redirect.
What have you modified in the AuthComponent?

(O)Auth with ExtJS

today i tried to get django-piston and ExtJS working. I used the ExtJS restful example and the piston example to create a little restful webapp. Everything works fine except the authentication.
Whats the best way to get Basic/Digest/OAuth authentication working with ExtJS?
Atm I'm not sure where to set the Username/Password.
Thanks
If you want to use piston with ExtJS, I would suggest writing an anonymous handler that checks the user is logged in via standard auth.
Try this:
class AnonymousUserProfileHandler(BaseHandler):
fields = ('title', 'url', 'affiliation')
model = UserProfile
def read(self, request, nickname):
profile = UserProfile.objects.get(nickname=nickname)
if request.user == profile.user:
return profile
class UserProfileHandler(BaseHandler):
anonymous = AnonymousUserProfileHandler
allowed_methods = ('GET')
fields = ('title', 'url', 'affiliation')
model = UserProfile
def read(self, request, nickname):
profile = UserProfile.objects.get(nickname=nickname)
return profile
In this example, when UserProfileHandler is called, without any authorization, it delegates to the anonymous handler. The anonymous handler checks whether the user is logged in via the usual request.user mode. If there is a valid user, it returns their profile object. You would then, obviously, mark the view calling this as requiring login.
The point is: when extJS makes its JSON call, it will send authentication data via the usual cookie. If you use an "anonymous" handler in Piston, but manually check the user is logged in before returning the data, then you essentially use traditional auth for your own site.

How do I detect the environment in Salesforce?

I am integrating our back end systems with Salesforce using the web services. I have production and stage environments running on different URLs. I need to be able to have the endpoint of the web service call be different depending on whether the code is running in the production or sandbox Salesforce instance.
How do I detect the environment.
Currently I am considering looking up a user to see if there user name ends in 'devsandbox' as I have been unable to identify a system object that I can query to get the environment.
Further clarification:
The location I need to determine this is within the Apex code that is invoked when I select a button in Salesforce. My custom controller needs to know if it running in the production or sandbox Salesforce environment.
For y'all finding this via search results, there is an important update. As Daniel Hoechst pointed out in another post, SF now directly provides sandbox vs. production information:
In Summer '14, (version 31.0), there is a new field available on the
Organization object.
select Id, IsSandbox from Organization limit 1
From the release notes under New and Change Objects:
The Organization object has the following new read-only fields.
InstanceName
IsSandbox
Based on the responses it appears that Salesforce does not have a system object that can tell me if my Apex code is running in production or a sandbox environment.
I am going to proceed based on the following assumptions:
I can read the organisation id of the current environment
The organisation id of my production system will always remain constant.
The organisation id of a sandbox will always be different to production (as they are unique)
The current organization ID can be found with System.getOrganizationId()
My solution is to have my code compare the current org id to the constant value representing production.
I'm performing necromancy here and the answer is already accepted, but maybe somebody will benefit from it...
Use one of these merge fields on your Visualforce page / S-Control:
{!$Api.Enterprise_Server_URL_180}, {!$Api.Partner_Server_URL_180}, {!$Api.Session_ID}
You can easily parse out organization ID out of them.
In Apex code: UserInfo.getOrganisationId()
I know this is an old post, but just for the sake of people looking for an updated answer as of Spring '11 release there is a new method System.URL.getSalesforceBaseUrl().toExternalForm() that returns the current url.
You can work from there to get all the info you need.
Here's the link to docs: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_methods_system_url.htm
The Login API call returns a sandbox element in the returned LoginResult structure that indicates if its a sandbox environment or not, from the WSDL.
<complexType name="LoginResult">
<sequence>
<element name="metadataServerUrl" type="xsd:string" nillable="true"/>
<element name="passwordExpired" type="xsd:boolean" />
<element name="sandbox" type="xsd:boolean"/>
<element name="serverUrl" type="xsd:string" nillable="true"/>
<element name="sessionId" type="xsd:string" nillable="true"/>
<element name="userId" type="tns:ID" nillable="true"/>
<element name="userInfo" type="tns:GetUserInfoResult" minOccurs="0"/>
</sequence>
</complexType>
Sandboxes may have a personalized url (e.g. acme.cs1.my.salesforce.com), or might be hosting a visualforce page (cs2.visual.force.com) or both (acme.cs2.visual.force.com) so I use this method:
public static Boolean isRunningInSandbox() {
String s = System.URL.getSalesforceBaseUrl().getHost();
return (Pattern.matches('(.*\\.)?cs[0-9]*(-api)?\\..*force.com',s));
}
I think the easiest way to do this would be to create a custom object in Salesforce, and then store a value indicating sandbox or production there. Your Apex code can then query that object. One suggestion would be to use Apex static constructors to load this information and cache it for the request.
Another thought I had (but hate to suggest) is to use an external service to determine where your Apex code is executing. This would probably be difficult to pull off, as every time the SalesForce server farm changes there is a change your code would break, but I just thought I'd throw this out there.
HttpRequest req = new HttpRequest();
req.setEndpoint('http://www.whatismyip.com/automation/n09230945.asp');
req.setMethod('GET');
Http http = new Http();
HTTPResponse res = http.send(req);
System.debug(res.getBody());
You have to add "http://www.whatismyip.com" to the Remote Site settings to get this to work (Setup > Administration Setup > Security Controls > Remote Site Settings). This code should run in the debug window when you click "System Log".
In your apex code you can use the following to get the instance of SF that you are in.
Keeping it dynamic will make sure you don't have to update your code when your org is migrated to a different instance.
String s = System.URL.getSalesforceBaseUrl().getHost();
//this will return "na1.salesforce.com" or "na1-api.salesforce.com",
// where na1 is your instance of SF, and -api may be there depending on where you run this
s = s.substring(0,s.indexOf('.'));
if(s.contains('-'))
{
s = s.substring(0,s.indexOf('-'));
}
system.debug(s);
There is a similar question on the Salesforce StackExchange for detecting if you are in a Sandbox or not - Can we determine if the Salesforce instance is production org or a Sandbox org?
In the solutions in search of a problem category, you could use the pod identifier from the OrgId to determine if you are dealing with a sandbox.
string podId = UserInfo.getOrganizationId().substring(3,4);
boolean isSandbox = 'TSRQPONMLKJZVWcefg'.contains(podId);
System.debug('IsSandbox: ' + isSandbox);
Caveat Confector: The big weakness here is that you will need to update the list of know sandbox pods as and when Salesforce brings new ones online (so it might be safer sticking with the other solutions).
You can use the following code block from Michael Farrington an authority on Salesforce.
Original blog post here: Michael Farrington: Where Am I Method
This method will return true if you are in a test or sandbox environment and false otherwise.
public Static Boolean isSandbox(){
String host = URL.getSalesforceBaseUrl().getHost();
String server = host.substring(0,host.indexOf('.'));
// It's easiest to check for 'my domain' sandboxes first
// even though that will be rare
if(server.contains('--'))
return true;
// tapp0 is a unique "non-cs" server so we check it now
if(server == 'tapp0')
return true;
// If server is 'cs' followed by a number it's a sandbox
if(server.length()>2){
if(server.substring(0,2)=='cs'){
try{
Integer.valueOf(server.substring(2,server.length()));
}
catch(exception e){
//started with cs, but not followed by a number
return false;
}
//cs followed by a number, that's a hit
return true;
}
}
// If we made it here it's a production box
return false;
}

Resources