Need to understand the Logic of Playframework - database

i have a simple question. if i use play.db.ebean.Model to have my Model extend from Model, how can i save it into DB? more clearly: in Django, the database file is created and i save the object, it will be saved into db file and i dont do any sql statements for retrieving or saving objects.. how does this work in playframework?
lets say i have configured my database file in application.conf file like this:
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:tcp://localhost/~/microblogdb"
db.default.user="sa"
db.default.password=""
now i have a database file somewhere in system.
now i have a Class User which extends Model as i stated above. now i want to save one User object into db. so i will do like this:
User user = new User();
user.username = "testusername";
user.fullname = "userfullname";
user.save();
what will happen after that save() call? can i now directly see my User object in database file?
appreciate any help!
many thanks

Yes it will, if you didn't do any mistakes.
Make sure that in application.conf you also UNcommented the line:
ebean.default="models.*"
Check sample applications in sample folder of the Play package you downloaded. For an example ComputerDatabase (not JPA version) to see the basics of working with Ebean.
What's more you can create constructor(s) in your model to simplify creating User model:
public User(String username, String fullname) {
this.username = username;
this.fullname = fullname;
}
And use it in controller as:
User user = new User("doniyor", "Doniyor The Great");
user.save();

Related

Laravel 8 Fortify User UUID Login Problem

I am currently setting up a new project using Laravel 8. Out of the box, Laravel is configured to use auto-incrementing ID's for the user's ID. In the past I have overrode this by doing the following.
Updating the ID column in the user table creation migration to
$table->uuid('id');
$table->primary('id');
Adding the following trait
trait UsesUUID
{
protected static function bootUsesUUID()
{
static::creating(function ($model) {
$model->{$model->getKeyName()} = (string) Str::orderedUuid();
});
}
}
Adding the following to the user model file
use UsesUUID;
public $incrementing = false;
protected $primaryKey = 'id';
protected $keyType = 'uuid';
On this new project, I did the same as above. This seems to break the login functionality. When the email and password are entered and submitted, the form clears as though the page has been refreshed. Thing to note is there are no typical validation error messages returned as would be expected if the email and/or password is wrong.
To check that the right account is actually being found and the password is being checked properly, I added the following code to the FortifyServiceProvider boot method. The log file confirms that the user is found and the user object dump is correct too.
Fortify::authenticateUsing(function(Request $request) {
\Log::debug('running login flow...');
$user = User::where('email', $request->email)->first();
if ($user && Hash::check($request->password, $user->password)) {
\Log::debug('user found');
\Log::debug($user);
return $user;
}
\Log::debug('user not found');
return false;
});
Undoing the above changes to the user model fixes the login problem. However, it introduces a new problem that is the login will be successful but it wont be the right account that is logged in. For example, there are 3 accounts, I enter the credentials for the second or third account, but no matter what, the system will always login using the first account.
Anyone have any suggestions or ideas as to what I may be doing wrong, or if anyone has come across the same/similar issue and how you went about resolving it?
Thanks.
After digging around some more, I have found the solution.
Laravel 8 now stores sessions inside the sessions table in the database. The sessions table has got a user_id column that is a foreign key to the id column in the users table.
Looking at the migration file for the sessions table, I found that I had forgot to change the following the problem.
From
$table->foreignId('user_id')->nullable()->index();
To
$table->foreignUuid('user_id')->nullable()->index();
This is because Laravel 8 by default uses auto incrementing ID for user ID. Since I had modified the ID column to the users table to UUID, I had forgotten to update the reference in the sessions table too.

Flask-admin get data sen

Task: I would like to catch data sending by flask-admin to database after fill in forms in admin panel.
I have overridden default User view as follows:
from flask_admin.contrib.sqla import ModelView
class UserModelView(ModelView):
def is_accessible(self):
return current_user.is_active and current_user.is_authenticated and current_user.has_role("admin")
def _handle_view(self, name, **kwargs):
if not self.is_accessible():
return redirect(url_for("admin.index"))
All modifications on admin panel after save are going to database (flask-sqlalchemy) and it works fine.
Now I would like to catch the data that is sending, for example:
I have changed username from X to Y and click "save".
I want to catch that piece of information with username = "Y" sending to database.
In docs I saw that there is method:
on_model_change(form, model, is_created)[source]
Perform some actions before a model is created or updated.
So I think this is a place where I should put my code, but question is, how can I get that data sending from there to database?
Normally, when I fill in something on web, its form defined by me so I just use data from this form, but here its defined by flask-admin and I have to idea how to access there.
Any ideas?

Invalidating reference tokens from code (not http call to revocation endpoint)

I am persisting reference tokens to a db, my users have the ability to change or get a generated password. But if for example a user have forgotten their password and gets a new generated one then i would like to invalidate/remove all current tokens for this subject. Is it a good idea/acceptable to interact directly with the db via efcore or is there a api for this besides the /connect/revocation endpoint?
There is no problem in interacting with the database, but use the existing services to do this.
In IdentityService you can find the stores in the IdentityServer4.Stores namespace.
using IdentityServer4.Stores;
Inject the store in your controller:
private readonly IReferenceTokenStore _referenceTokenStore;
public class MyController : Controller
{
public MyController(IReferenceTokenStore referenceTokenStore)
{
_referenceTokenStore = referenceTokenStore;
}
}
And call it to remove the reference tokens for this user / client combination:
await _referenceTokenStore.RemoveReferenceTokensAsync(subjectId, clientId);
This will effectively remove the records from the database. You shouldn't create your own model of the database and remove the tokens directly.
Since IdentityServer is open source, you can take a look at the code that is used for token revocation.

wordpress function with custom database

I am writing a WordPress plugin.
In one program, I capture the WordPress user id and write it to a file in a custom database.
Another program connects to the custom database, retrieves multiple rows having the user id:
$connection = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
$sql = "SELECT ...";
$prep = $connection->prepare($sql);
and tries to access the wordpress function after retrieving each record:
$user_info = get_userdata($user_id);
As soon as the get_userdata function is executed, the programs dies.
Do I need to connect to the wordpress database?
If so, how?
First of all, Why you have made database connection manually this way as you can able to use global variable "global $wpdb;" and then able to write query based on "$wpdb".
Second thing, you might declare that $user_id variable as global, so can access it globally in file or you should define in functions.php file and check for their availability.
Please let me know if any of this solution does not work for you with details.

How to handle security/authentication on a DNN-based web API

I am building a REST API for a DotNetNuke 6 website, making use of DNN's MVC-based Services Framework. However, I don't have any background in authentication, so I'm not even sure where to start.
Basically, we want our clients to be able to make GET requests for their portal's data, and we want some clients (but not all) to be able to POST simple updates to their user data.
I've been trying to search for information, but the trouble is I'm not sure what I'm searching for. DNN has different logins and roles, but I'm not sure if or how they factor in. I've heard of things like oAuth but my understanding of it is at the most basic level. I don't know if it's what I need or not and if or how it applies to DNN. Can anyone point me in the right direction?
UPDATE:
Based on the answer below about tying it with a module and further research, here is what I have done:
I created a module just for this service, and I added two special permissions for it: "APIGET" and "APIPOST." I assigned these to some test roles/test accounts in DNN. I wrote a custom authorize attribute that, given the module ID, checks if the current user has the necessary permission (either through roles or directly). As far as I can tell, tab ID is irrelevant in my case.
It appears to be working both with a web browser (based on the DNN account I'm logged into) and with a php script that sends an HTTP request with an account username/password.
The authorize attribute:
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Security;
using DotNetNuke.Security.Permissions;
using System.Web;
public class MyAuthorize : DotNetNuke.Web.Services.AuthorizeAttributeBase
{
public const string AuthModuleFriendlyName = "MyAuthModule";
public const string GETPermission = "APIGET";
public const string POSTPermission = "APIPOST";
public string Permission { get; set; }
protected override bool AuthorizeCore(HttpContextBase context)
{
ModuleController mc = new ModuleController();
ModuleInfo mi = mc.GetModuleByDefinition(PortalController.GetCurrentPortalSettings().PortalId, AuthModuleFriendlyName);
ModulePermissionCollection permCollection = mi.ModulePermissions;
return ModulePermissionController.HasModulePermission(permCollection, Permission);
}
}
The controller:
("mytest" is the endpoint for both GET and POST)
public class MyController : DnnController
{
[ActionName("mytest")]
[AcceptVerbs(HttpVerbs.Get)]
[DnnAuthorize(AllowAnonymous = true)]
[MyAuthorize(Permission = MyAuthorize.GETPermission)]
public string myget(string id = "")
{
return "You have my permission to GET";
}
[ActionName("mytest")]
[AcceptVerbs(HttpVerbs.Post)]
[DnnAuthorize(AllowAnonymous = true)]
[MyAuthorize(Permission = MyAuthorize.POSTPermission)]
public string mypost(string id = "")
{
return "You have my permission to POST";
}
}
The main way that you tie a service in the DNN Services Framework into DNN permissions is to associate the permissions with a module instance. That is, you'll require users of your service to identify which module they're calling from/about (by sending ModuleId and TabId in the request [headers, query-string, cookies, form]), then you can indicate what permissions they need on that module to take a particular action on the service.
You can use the SupportedModules attribute on your service, and pass in a comma-delimited list of module names, to ensure that only your own modules are being allowed. Then, add the DnnModuleAuthorize attribute at the service or individual action level to say what permission the user needs on that module. In your instance, you can also add the AllowAnonymous attribute on the GET actions, and have one DnnModuleAuthorize on the service, for the POST methods (and anything else). Note that you cannot put the AllowAnonymous attribute on the controller; it will override authorizations put at the action, making it impossible to make actions more restrictive.
You'll also want to add the ValidateAntiForgeryToken attribute on the POST actions, to protect against CSRF attacks.
If you don't have a module that naturally associates its permissions with your service, you can create one just for that purpose, solely to expose itself as a permissions management utility.
Once you've figured out the authorization piece above, DNN will take care of authentication using your forms cookie (i.e. AJAX scenarios are taken care of automatically), or via basic or digest authentication (for non-AJAX scenarios). That said, if you're doing non-AJAX, you'll need to figure out a way to validate the anti-forgery token only when it applies.
The Services Framework in DNN is what you are after. It allows you to provide a REST API that plugs directly into DNN security.
Here are some articles to help you get started:
http://www.dotnetnuke.com/Resources/Wiki/Page/Services-Framework-WebAPI.aspx
http://www.dotnetnuke.com/Resources/Blogs/EntryId/3327/Getting-Started-with-DotNetNuke-Services-Framework.aspx
Note, there are some difference in DNN 6 and DNN 7 when using the Services Framework:
http://www.dotnetnuke.com/Resources/Blogs/EntryId/3514/Converting-Services-Framework-MVC-to-WebAPI.aspx
Just wanted to note that the DnnModuleAuthorize attribute takes a PermissionKey parameter for custom permissions so you can do stuff like this:
[DnnModuleAuthorize(PermissionKey = "DELETEDATA")]
[HttpPost]
public HttpResponseMessage DeleteData(FormDataCollection data)
It doesn't look like you can supply your own error message with this so you might to wrap your method body like this instead and leave off the custom permission attribute:
[DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.View)]
[HttpPost]
public HttpResponseMessage DeleteData(FormDataCollection data)
{
var errorMessage = "Could not delete data";
if (ModulePermissionController.HasModulePermission(ActiveModule.ModulePermissions,"DELETEDATA"))
{
// do stuff here
}
else
{
errorMessage = "User does not have delete permission";
}
var error = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content =
new StringContent(
errorMessage)
};
return error;
}
Just wanted to add to #Richards comment for using the [DnnModuleAuthorize(PermissionKey = "DELETEDATA")] for custom permissions.
The full attribute should be:
[DnnModuleAuthorize(PermissionKey = "DELETEDATA", AccessLevel = SecurityAccessLevel.Edit)]
Leaving it blank does nothing as shown here: https://github.com/dnnsoftware/Dnn.Platform/blob/f4a5924c7cc8226cfe79bbc92357ec1a32165ada/DNN%20Platform/Library/Security/Permissions/PermissionProvider.cs#L810
I guess you require a plugin that allows you to construct GET and POST APIs. you can use this plugin I found on the DNN store. https://store.dnnsoftware.com/dnn-rest-api-custom-api-authentication-authorization.

Resources