all.
I am having difficulties to write a test for my model with default scope. I have a user model with attribute active_status which can be changed by admin. I gave the user default scope active_status true. default_scope(:active_status => true ) In Admin page I used unscoped so that Admin can see all the users.
How to write this test?
Thanks
you could so something along the lines of this:
context "scopes" do
describe "default_scope" do
before do
create(:user, active_status: true)
create(:user, active_status: false)
}
context "with default scope" do
subject { User.all }
it { should have(1).user }
end
context "without default scope" do
subject { User.unscoped.all }
it { should have(2).users }
end
end
end
note: I'm using RSpec and FactoryGirl.
note2: You can consider this as an anti pattern, because the default_scope is a protected method. But this is what you asked for.
Sounds like all you need to do is write a test for the admin page that creates an inactive user and verifies that when the admin page loads, one result is returned... Am I missing something?
Related
I am using Laravel 5.1 and Laravel's default authentication system.
In database (MySQL) I add a new column named 'role'. The value will be 1 for admin and 2 for members.
Now I want to give login permission only for admin, means where the value is 1. How can I do that?
Actually I solved it. I just add these code in postLogin() method of AthenticatesUsers.php method.
// If role is equal to 1, user allowed to login
// You can change $admin value anytime according to database Design
// Example: In role column the value for admin is 2 or A. You just need to change the value of $admin.
$userData = User::select('role')->where('email',$request['email'])->first();
$admin = 1;
$role = $userData->role;
if($role == $admin){
$request['role'] = $role;
}
I feel that there are better ways to achieve what you're after, such as middleware, however given what you're after this would be one way to do it.
Upon logging in a user us sent to 'home', unless you specify otherwise in the AuthController.
Inside your routes.php, if you just set up a GET route to point to a HomeController (or whatever you name it) then you could use a function to run the tests you're after.
routes.php
Route::get('home', 'HomeController#index');
HomeController
public function index()
{
//If they are yet to log in then return your normal homepage
if (Auth::guest())
{
return View::make('home');
}
else
{
//Run your tests here to check their role and direct appropriately
//Given you have added the role column to the users table, you can access it like so:
//Auth::user()->role
}
}
Meteors loginWithPassword() function doesn't provide me the object systemData, which I adding to user doc (not to profile obj) during registration. The thing is, that if I look into console after logging in, I can see that object systemData (that means probably it's not publish issue), but not in callback of loginWithPassword() function, where I need them (to dynamically redirect user to proper page). Is there way to get this object, without any ugly things like timers?
Meteor.loginWithPassword(email, password, function(errorObject) {
if (errorObject) {
...
} else {
// returns true
if (Meteor.userId()) {
// returns false
if (Meteor.user().systemData) {
...
}
// user doc without systemData object
console.log(JSON.stringify(Meteor.user());
}
}
I've adding object systemData on creating user:
Accounts.onCreateUser(function(options, user) {
if (options.profile) {
user.profile = options.profile;
}
...
user.systemData = systemDataRegularUser;
return user;
});
Are you sure publish data to Client ?
I get User Info Using loginWithPassword in callback function.
Meteor.loginWithPassword username,password,(error,result1)->
options =
username: username
password: password
email: result['data']['email']
profile:
name: result['data']['display-name']
roles: result.roles
console.log Meteor.user(), result1
I Create user flowing code: (options contains systemData)
Accounts.createUser option
The first problem is that you want a custom field on a user document published to the client. This is a common question - see the answer here.
The next problem is that even after you add something like:
Meteor.publish("userData", function () {
return Meteor.users.find(this.userId, {fields: {systemData: 1}});
});
I think you still have a race condition. When you call loginWithPassword, the server will publish your user document, but it will also publish another version of the same document with the systemData field. You are hoping that both events have completed by the time Meteor.user() is called. In practice this may just work, but I'm not sure there is any guarantee that it always will. As you suggested, if you added a slight delay with a timer that would probably work but it's an ugly hack.
Alternatively, can you just add systemData to the user's profile so it will always be published?
I didn't find exact way how to solve this, but found easy workaround.
To make some action right after user logged in (eg. dynamically redirect user to proper page), you can hook on your home page with Iron router.(If you using it.) :
this.route('UsersListingHome', {
path: '/',
template: 'UsersListing',
data: function() { ... },
before: function() {
if (isCurrentUserAdmin() && Session.get('adminJustLogged') !== 'loggedIn') {
Router.go('/page-to-redirect');
Session.set('adminJustLogged','loggedIn');
}
}
});
After click on logout of course if (isCurrentUserAdmin()) { Session.set('adminJustLogged', null); }
I've further thought about calling Meteor.call('someMethod') to fetch userData object in Method callback, but now I'm satisfied.
PS: I know that it's not recommended to have plenty session variables or other reactive data source for speeding-up my app, but I believe, that one is not tragedy :)
PS2: Anyway, thanks for your answers.
Some of my tests don't work because of saved data in my database. I found out (code below) that the create function of FactoryGirl doesn't go through the controller steps to ensure that everything defined in the controller is passed. One thing that my controller does is format an attribute before saving to database to ensure that a string is saved and not an array, e.g. "Monday,Tuesday" instead of ["Monday,Tuesday"].
if Schedule.count == 0
FactoryGirl.create(:schedule)
end
How do I set the before statement to make the create functionality execute controller steps?
Solved this by putting the following line:
before { FactoryGirl.create(:default_for_overlap_schedule) }
Full test is:
describe "has schedule overlap" do
before { FactoryGirl.create(:default_for_overlap_schedule) }
let!(:overlap_schedule) { FactoryGirl.attributes_for(:overlap_schedule) }
it "prompts user of error" do
expect{ post :create, schedule: overlap_schedule }.not_to change(Schedule, :count).by(1)
flash[:error].should eq("Schedule has an overlap")
end
end
I'm moving some of my find code inside models.
Previously in my controller I had
$this->Book->Review->find('first', array(
'conditions' => array(
'Review.book_id' => $id,
'Review.user_id' => $this->Auth->user('id')
)
));
so in my Review model I put something like
function own($id) {
$this->contain();
$review = $this->find('first', array(
'conditions' => array(
'Review.book_id' => $id,
'Review.user_id' => AuthComponent::user('id')
)
));
return $review;
}
So I'm calling AuthComponent statically from the Model. I know I can do this for the method AuthComponent::password(), which is useful for validation. But I'm getting errors using the method AuthComponent::user(), in particular
Fatal error: Call to a member function
check() on a non-object in
/var/www/MathOnline/cake/libs/controller/components/auth.php
on line 663
Is there a way to get the info about the currently logged user from a model?
Create a new function in the "app_model.php" ("AppModel.php" in CakePHP 2.x), so it will be available at all models within our application:
function getCurrentUser() {
// for CakePHP 1.x:
App::import('Component','Session');
$Session = new SessionComponent();
// for CakePHP 2.x:
App::uses('CakeSession', 'Model/Datasource');
$Session = new CakeSession();
$user = $Session->read('Auth.User');
return $user;
}
in the model:
$user = $this->getCurrentUser();
$user_id = $user['id'];
$username = $user['username'];
The way that I use is this:
App::import('component', 'CakeSession');
$thisUserID = CakeSession::read('Auth.User.id');
It seems to work quite nicely :-)
I think the code is fine as it is and belongs in the Controller, or at the very least it needs to receive the ids from the Controller and not try to get them itself. The Model should only be concerned with fetching data from a data store and returning it. It must not be concerned with how the data is handled in the rest of the application or where the parameters to its request are coming from. Otherwise you paint yourself into a corner where the ReviewModel can only retrieve data for logged in users, which might not always be what you want.
As such, I'd use a function signature like this:
function findByBookAndUserId($book_id, $user_id) {
…
}
$this->Review->findByBookAndUserId($id, $this->Auth->user('id'));
There is a nice solution by Matt Curry. You store the data of the current logged user in the app_controller using the beforeFilter callback and access it later using static calls. A description can be found here:
http://www.pseudocoder.com/archives/2008/10/06/accessing-user-sessions-from-models-or-anywhere-in-cakephp-revealed/
EDIT: the above link is outdated: https://github.com/mcurry/cakephp_static_user
I think this is not good idea to get value from Session. Better solution to get logged user id inside any model simply try this:
AuthComponent::user('id');
This will work almost every where. View, Model and Controller
Dirtiest way would be to just access the user information in the Session. Least amount of overhead associated with that.
The "proper" way would probably be to instantiate the AuthComponent object, so that it does all the stuff it needs to be fully operational. Much like a death star, the AuthComponent doesn't really work well when not fully setup.
To get a new AC object, in the model:
App::import( 'Component', 'Auth' );
$this->Auth = new AuthComponent();
Now you can use $this->Auth in the model, same as you would in the controller.
For CakePHP 3.x this easy component is available: http://cakemanager.org/docs/utils/1.0/components/globalauth/. Direct accessing the Session is not possible because of different SessionKeys.
With the GlobalAuthComponent you can access your user-data everywhere with: Configure::read('GlobalAuth');.
Greetz
Bob
I use cake 2.2 and these both work great:
$this->Session->read('Auth.User');
//or
$this->Auth->user();
You can also get a field of currently logged in user:
$this->Session->read('Auth.User.email');
//or
$this->Auth->user()['email'];
None of these solutions work in CakePHP version 3. Anyone know of a way to do this? Right now, I'm completely stepping around the framework by accessing the $_SESSION variable directly from my model.
I'm writing a small webapp in Grails, and to make sure all users are authenticated I'm using the following filter:
class LoginFilters {
static filters = {
loginCheck(controller:'*', action:'*') {
before = {
if (session.user_id) {
request.user = User.get(session.user_id)
} else if (!actionName.equals("login")) {
redirect(controller: "login", action: "login")
return false
}
}
}
}
}
And all controller methods start with reading the user property of the request object:
def actionName = {
def user = request.user
...
}
The code above works, but I'd rather avoid the duplicate code in the all controller methods. Would it be possible for the filter to bind the user object to a variable named "user" instead of "request.user", that will be accessible from all controllers?
I understand that there might be scoping issues that makes this impossible, but the Grails framework seems to be able to create quite some magic under the hood, so I figured it might be worth asking.
Using the beforeInterceptor in a controller may help:
class LoginController {
def user
def beforeInterceptor = {
user = request.user
}
def index = {
render text:"index: ${user}"
}
def test = {
render text:"test: ${user}"
}
}
I think it generally not a good idea insert the user object into the request object every time:
The request lifetime is very short, so you might end up making round trips to caches or even worse to the database on each http-request to retrieve an object, that you might not even need and that get's deleted immideately afterwards. So if you must, better store the whole object in the session instead of just the id.
Generally, I'd suggest you write a AuthenticationService with a method isLoggedIn() that returns true when the user is authenticated and a method getLoggedInUser() that returns this object.
class AuthenticationService {
def transactional = false
boolean isLoggedIn() { return session.user_id }
def getLoggedInUser() { return User.get(session.user_id) }
}
Then you use the Filter for redirection if not authenticated, and maybe the Interceptor for storing the local reference user = authenticationService.loggedInUser. But also I don't think this the best way to go. I suggest you'd create an abstract AuthenticationAwareController as base class for all your controllers in src/groovy and there have the convenience method like user
class AuthenticationAwareController {
def authenticationService
def getUser() { return authenticationService.loggedInUser() }
}
This way, you can later change you mind about storing the user however you like and don't have to change your code. Also you benefit from Caches in Hibernate, that share already retrieved user object instances between different sessions, so db roundtrips are avoided.
You still should check the retrieved user object for validity or throw an AuthenticationException in case the retrieval does not succeed. (Maybe something like AuthenticationService.getLoggedInUser(failOnError = false).)
You can even make this Service/ControllerBase a small plugin an reuse that on every application or go directly with the spring security plugin... ;-)
I think you can do this but is it really worth the trouble? It seems to me your only advantage is typing "user" instead of "request.user". Not a big gain. Anyway, I think you could follow the instructions in "12.7 Adding Dynamic Methods at Runtime" of the User Guide. I think that if you created a dynamic method "getUser() {return request.user}" that the Groovy JavaBeans getter/setter access would allow you to simply reference "user" the way you want.
If you do add a dynamic method you might want to skip the filter and do it all in the dynamic method.