ACL implementation for databases - php

I'm trying to implement an ACL in my database. I have a database which looks like:
SYS_USERS { id, name, .. }
AUTH { au_id, au_name, .. }
USER_GROUPS { sys_users_id, auth_id } // bridge table
And say AUTH data looks like:
au_id au_name ...
1 admin ...
2 staff ...
... ... ...
My question is, how can I structure my query from php such that upon login, depending on your authentication level, you are presented different pages?
At the moment I have this, which seems a little off:
<?php
// code which verifies session variables etc here
$mysql_hostname = 'localhost';
$mysql_username = '...';
$mysql_password = '...';
$mysql_dbname = '...';
try {
/* Set up new DB object */
$db = new PDO("mysql:host=$mysql_hostname;dbname=$mysql_dbname", $mysql_username, $mysql_password);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
/* authenticate user access level */
// username = 'blah' for question clarity
$query = $db->prepare('SELECT au_name FROM auth WHERE au_id = (SELECT auth_id FROM user_groups WHERE sys_users_id = (SELECT id FROM sys_users WHERE username="blah"))');
// do something with results..
} catch(Exception $exception) {
$message = 'We are unable to process your request. Please try again later';
}
?>
So I guess my questions are:
Is the SELECT query adequate? Or should I use an INNER JOIN to achieve the same results? (Does it matter?)
Upon success of the query, how do I show the page depending on the level? For example, if it returned admin do I write a function such that
if ($result == 'admin') {
// show admin.php
} elseif ($result == 'staff') {
// show staff.php
} else { ... }
But this seems rather 'hard coded', i.e. if we were to extend the system for more AUTH roles, we would need to then add in more IF/ELSEIF statements to the above.
Anyone able to lead me in the right direction?
Thanks.
EDIT
So I was thinking of a new way to do this. I could add two more database tables:
PAGES { id, page_name .. }
AUTH_PAGES { au_id, pages_id, .. } // bridge between auth and pages
Which then in pages I could store page_name which would hold the authentication level required to view that page?
For example: a page called admin_page.php could only be accessed by administrators. Therefore this row in pages looks like:
id page_name
1 admin_page.php
2 members_page.php
and auth_pages:
au_id pages_id
1 1
1 2
Which is to say the auth row admin (au_name) has access to admin_page.php and members_page.php. Then all I would need to do in my PHP would be to cross reference the page name with the id from pages table with auth_pages table using echo basename($_SERVER['PHP_SELF']);.
Does that make any practical sense?

Since you mentioned its going to be simple so this is what I can suggest you.
At the time of login get the user id and then run a query with the id user as
select
a.au_name,a.au_id
from USER_GROUPS ag
inner join SYS_USERS su on su.id = ag.sys_users_id
inner join AUTH a on a.au_id = ag.auth_id
where ag.sys_users_id = {id of the user retrieved after the login validation}
Now Execute the above query and get the au_name and store it in a session variable as
$_SESSION['au_name'] = {auth name from the above query} ;
Create a function as below and execute it after the login.
get_page_access($au_id){
run a query to get all the pages for the auth id you got from previous query
store them in an array and finally to a session variable as
$_SESSION['page_access'] = $array ;
$array will hold all the pages you retrive
}
Now do the redirect based on the $_SESSION['au_name'] firstime after the login.
Now what if user hotlink an URL i.e. a non-admin user try to access a page. So for that create a file called check_access.php and add include it to all the pages other than the login page.
In this page you get the URL using PHP and get the filename from the URL, then check if that filename exists on the array $_SESSION['page_access'] and if yes user is allowed to view the page else show message.
Make sure you do session_start() before the include .
This will be fairly simple in nature

Related

Getting data from database based on user id

Hi I am trying to get data from the database where the user can click on a person name and be redirected to some other page to see the person detail example like "user id = 1". Any idea on how to do it? If possible could you show me some example on how to do it thanks
Below is a screenshot on what I have done so far, the name below are in href so that it can redirect the user to another page and see the person details
you can use Laravel Eloquent for this one.
User::find($userid);
or using Laravel Query Builder
$users = DB::table('users')where('id','=', $userid)->get();
You can write a function for redirect in javascript with parameter is User ID. After that use your routes to get parameter (user id) on your link and write a query mysql in your controller to get data of this user id. Finally , you bring all of data from this user to your view.
function Redirect(userid) {
window.location = 'http://yourdomain.com/profile/'+userid;
}
in your view add event onclick to username.
bob
in routes/web.php add new route:
Route::get('/{userid}','Controller#index');
in your controller:
$UserID=$request->input('userid');
now your parameter on your link will stored in $UserID. You can use this value to query data and show it on you view.
Here's a sample code of how to get the desired user from the data base.
$dbUser="root";
$dbPassword="root";
$dbServer="localhost:8889";
$dbName="test_database";
$connection = new mysqli($dbServer, $dbUser, $dbPassword, $dbName);
if($connection->connect_errno)
{
exit("Database Connection Failed. Reason: ".$connection->connect_error);
}else{
echo "Connection successful..;
}
$query = "SELECT id,first_name,last_name FROM Author where user_id= XXXX ";
$resultObj = $connection->query($query);
if ($resultObj->num_rows > 0)
{
while ($singleRowFromQuery = $resultObj->fetch_array())
{
print_r($singleRowFromQuery);
echo "Author: ".$singleRowFromQuery['first_name'].PHP_EOL."<br>";
}
}
$resultObj->close();
$connection->close();
Make sure you replace the user_id appropriately and provide appropriate declaration.
you also pass data through the call url in the anchor tag using .. url ..?user_id=XXX

run another query after successful login

I need some help with my login script. I am just trying to self teach php and mysqli and stuck with this so any advice/help would be much appreciated.
The actual part that checks the login details works and directs the user a page if the match is found or back to login page if unsuccessful.
What I want to happen is once the username has been checked and the sessions set use the session 'user' to then find the same user from either the pupil or instructor table depending on the session 'member_type'. The session 'user' is set as the 'user_id' which is the same in the login table and the table the rest of the details are stored in and is the primary key of both tables.
<?php
include_once ("includes/dbconnect.php");
session_start();
$error="";
if ( isset($_POST['btn_signin']) ) {
// username and password received from loginform
$username=mysqli_real_escape_string($conn,$_POST['username']);
$password=mysqli_real_escape_string($conn,$_POST['user_password']);
$sql_checklogin="SELECT * FROM user_logins WHERE username='$username' and password='$password'";
$result=mysqli_query($conn,$sql_checklogin);
$login=mysqli_fetch_array($result,MYSQLI_ASSOC);
$count=mysqli_num_rows($result);
// If result matched $username and $password, table row must be 1 row
if($count==1)
{
// Set Sessions
$_SESSION['logged_in']=TRUE;
$_SESSION['user']=$login['user_id'];
$_SESSION['member_type']=$login['reg_type'];
} else {
header("location:login.php");
$error = "Invalid Username or Password!";
}
}
//This part to be replaced with code that checks both tables to find the user details
if ( isset($_SESSION['member_type'])) {
header("Location:mydashboard.php");
}
?>
//The above code works and allows user to login but sends both member types to same page
Just now this works fine but I want to replace the last IF part of the script with something like this (or a better suggestion?).
//if ( isset($_SESSION['member_type'])=='pupil' {
//$sql_findpupil="SELECT * FROM pupils WHERE user_id='$_SESSION['user']'";
//$result=mysqli_query($conn,$sql_findpupil);
//$pupil=mysqli_fetch_array($result,MYSQLI_ASSOC);
Should I be putting another count function at this part?
//header("location:mydashboard.php");
//}elseif ( isset($_SESSION['member_type'])=='instructor' {
//$sql_findinstructor="SELECT * FROM instructors WHERE user_id='$_SESSION['user']'";
//$result=mysqli_query($conn,$sql_findinstructor);
//$instructor=mysqli_fetch_array($result,$MYSLQI_ASSOC);
And here?
//header("location:control_panel.php");
//}
//}
As I said, I am trying to teach myself some new skills at home so have probably made some basic school boy errors here and missed something obvious
isset() checks if a variable is set or not, but you want to check the value. So your if ( isset($_SESSION['member_type'])) is always true, for pupils and instructors. Try
if ($_SESSION['member_type'] == 'pupil') {
header("Location:mydashboard.php");
}
instead.
And another suggestion, refactor your database-design. Better don't use an instructor and a pupil table containing userIds, but a userType-Table containing only two rows (pupil / instructor) and then add a userTypeId to your user-table. And also add a foreign key from user.userTypeId to userType.id to make sure every user is linked to an existing userType.
Using that design you won't need another query, to determine if a user is a pupil or an instructor.

User permissions custom cms

looking for some advice on my current setup and if there is a better approach (this is my first attempt at building an app like this) but I am at a point where I need user roles, only two so user and admin.
I have a users table and a groups table and also a join table which takes the ID from the users table and the Id from the groups table and I can allocate which group the user belongs too.
I am wanting to set a way to do a couple of things, prevent 'users' accessing the admin area by typing in the url /admin/index.php and also show a link on the index page if they are an admin and not see it it if they are a normal user.
Here is my code to demonstrate that I can show the users role title but struggling at this point:
<?php
$user = $_SESSION['user'];
try{
$results = $dbh->query("SELECT *
FROM groups
INNER JOIN user_group_link_table
ON groups.id = user_group_link_table.group_id
WHERE user_group_link_table.user_id = $user");
}catch(Exception $e) {
echo $e->getMessage();
die();
}
$group = $results->fetchAll(PDO::FETCH_ASSOC);
foreach($group as $groups){
echo
$groups["name"]
// show a link to admins that user do not see?
;}
?>
I am wondering if my approach is totally wrong?
UPDATE
<?
include('session.php');
if (!isset($_SESSION['user'])) {
header("Location: index.php");
}
if(!ini_get('date.timezone'))
{
date_default_timezone_set('GMT');
}
// This could be an include file for all admin pages
$isAdmin = false;
foreach($group as $groups){
if($groups['name'] === 'admin'){
$isAdmin = true;
break;
}
}
if(!$isAdmin){
header('Location: index.php'); // or some other arbitrary location
die();
}
?>
And I get this error:
Notice: Undefined variable: group in /Applications/MAMP/htdocs/dashboardr v3.2.3/admin/header.php on line 12
Warning: Invalid argument supplied for foreach() in /Applications/MAMP/htdocs/dashboardr v3.2.3/admin/header.php on line 12
I am wanting to set a way to do a couple of things, prevent 'users' accessing the admin area by typing in the url /admin/index.php and also show a link on the index page if they are an admin and not see it it if they are a normal user.
I'll give you a solution that'll work in your current setup; It's not the way to go, but i'll get the job done for now. If you're worried about the user being in a specific role, you're better off specifying that role in your query rather than iterating through all their potential roles.
// This could be an include file for all admin pages
$isAdmin = false;
foreach($group as $groups){
if($groups['name'] === 'admin'){
$isAdmin = true;
break;
}
}
if(!$isAdmin){
header('Location: index.php'); // or some other arbitrary location
die;
}
You'll want to place this at the top of the page, before you spit out any kind of HTML.

What query do I need to determine if a user has favourited another?

I apologise for the simpleton question but I am having a complete blank, hence why the wording of the title is vague.
I have built a simple PHP/MySQL user favouriting system that works fine, except for one part. Once the user has favourited another user, I cannot think for the life of me how to show the user that they have already favourited that user.
This is what I have so far:
if (($user_data['username'] == $profile_data['username']) === false) {
if () { ?>
// Favourite user
<?php } else { ?>
//See whether favourited or unfavourite user
<?php } } ?>
The table structure of favourites is simply two columns, favouritee being the profile favourited and favouriter being the current user favouriting. This table is joined to a main users table and the columns are populated by username strings.
EDIT
This is what I have got so far:
$username = $user_data['username'];
$favouritee = $profile_data['username'];
$check_fav = mysqli_query("SELECT `id` FROM `favourites` WHERE (`favouritee` = '$favouritee' AND `favouriter` = '$username')");
if (mysqli_num_rows($check_fav) == 1) {
// Favourite user
} else {
//See whether favourited or unfavourite user
}
(Posted on behalf of the OP):
Working code:
if (($user_data['username'] == $profile_data['username']) === false) {
$username = $user_data['username'];
$favouritee = $profile_data['username'];
$check_fav = mysqli_query("SELECT `id` FROM `favourites` WHERE (`favouritee` = '$favouritee' AND `favouriter` = '$username')");
if (mysqli_num_rows($check_fav) == 1) {
// Favourite user
} else {
// Unfavourited/check
}
}
To find whether a user has favourited another user, assume $myUsername as the logged-in user's username from the session, and assume $otherUsername coming from a profile page of another user (or perhaps a paged list of users).
SELECT 1 FROM favourite
WHERE favouriter = :favouriter AND favouritee = :favouritee
You can then inject parameters $myUsername into :favouriter and $otherUsername into :favouritee and if you get a row, you already have a favourite connection, and if you get zero rows, there is no favourite connection.
This is just the raw query, so of course you'll need to add PHP database code around this. If you're not familiar with this, take a look at the docs for PDO or MySQLi at php.net - both sections will give enough information to get you up and running.
That said, assuming usernames are stored in the user table, I'd be inclined to switch the two columns in the favourite table to integer foreign keys - it'll be faster and will save disk space. I'd call these from_user_id and to_user_id to make it clear what they are foreign keys of, and the direction of the favourite.

How to log user actions with php and mysql?

I'm working on my CMS and I want it to log activities by users and other admins.
For example: when new user registers or admin makes a new news post -> update last activity.
I want to know what is the best and easiest way.
Create a table in your database to
log your user activity.
Define the various activity types
that can happen in your App.
Create a common function that logs
any activity to that table.
Call that function from anywhere
you perform log-worthy activities in
your app.
You can then write a reporting tool that gives your admins access to those logged activities, you can filter by user, time and activity types.
In my log-framework, I specially mark activities which could be seen as malicious actions and assign them different numeric threat-values. If the sum of a user's thread-value reaches a certain threshold I log-out the user.
Ideally if you write an Application, you write your infrastructure code like logging at the very beginning and then use it in all your business logic code later.
Edit for cleanup:
Over time you may collect lots of records in that table. Depending on your requirements you could do different things.
Delete any entries older than x days (maybe a year)
Delete any entries of certain types older than x days, but keep entries of other types for longer, or forever.
Move entries older than a certain threshold into an archive log table. This keeps your main table small but allows you to access older log data if you really have to. I have a checkbox Use archive on my review logs page.
Basic Answer
Instead of doing this yourself, from scratch, check out how some existing systems do it, and if their license allows, use their design and code (make sure you document what code you've used and add a copyright notice to your CMS somewhere).
Possibly Helpful Example
I'm not sure about PHP CMS's which do this, but I know Django's admin app does. Django is implemented in Python, but it should be fairly straightforward to port this code over to PHP. Even if the code isn't a straight port, the design could be ported.
The file which contains the logging is in the admin module in models.py.
Some key aspects:
The data model for the logging table:
class LogEntry(models.Model):
action_time = models.DateTimeField(_('action time'), auto_now=True)
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType, blank=True, null=True)
object_id = models.TextField(_('object id'), blank=True, null=True)
object_repr = models.CharField(_('object repr'), max_length=200)
action_flag = models.PositiveSmallIntegerField(_('action flag'))
change_message = models.TextField(_('change message'), blank=True)
objects = LogEntryManager()
And the LogEntryManager, which saves the actual log entries:
class LogEntryManager(models.Manager):
def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
e = self.model(None, None, user_id, content_type_id, smart_unicode(object_id), object_repr[:200], action_flag, change_message)
e.save()
I use two tables for activities, one that gives each activity an id, and another one that just logs the user id, activity id, and a timestamp. I do this because an int takes up less space than a string, so why log the same strings over and over? The second one isn't really necessary, you just just as easily keep the action codes in a text file for your own reference, but the db just seems like a easier place to remember.
In the past I've used a function to handle the actual logging actions, but the next time I do it I'm going to be using the Observer pattern. It appears to be a lot more flexible, and I've already had to edit out logging function calls from older code I have that wasn't going to log anything. I much prefer reusing code with no editing required.
Its very simple to do with PHP/JAVA FUNCTION JQUERY and its AJAX data posting method...
Before posting the solution -- Lets read these two lines
Why and What we want to record ?
--- As we know only to record transaction with in the database --not all the clicks and checks -- but yes its possible with this solution....
Here is the solution step by step: -
1. create a DB Table -- to record these things
a) Page Name.
b) logged in user name
c) session details (To record all the sessions).
d) POST/GET data details (To record all the post/get data for the
page)
e) Record Created Date.
or any other thing that you want to record.
2. Create a Jquery function or PHP function -- which will be auto triggered with every page.
3. This function will collect all the session of that page,user logged in details and what data passed to that page.
Apart from this - you can also record -- from which page this new page is called -- Its pretty simple and best way to implement loggs recording features even in already running old software's :)
If you want all the Code i mentioned above to use -- Search it over the NET the mechanism i have defined just you need FUNCTION CODE -- AUTO execute function code -- simple
PHP AND MYSQL
Create a Table to save the logs
CREATE TABLE `test_loq` (
id int(11) PRIMARY KEY AUTO_INCREMENT,
page varchar(255) NOT NULL,
username varchar(255) NOT NULL,
log_time datetime DEFAULT CURRENT_TIMESTAMP,
log_action longtext NOT NULL,
log_name varchar(255) NOT NULL,
user_id int(11) NOT NULL,
ip int(11) NOT NULL
)
explain :
log_action is the kind of action made here you can write a lot of information about the action that has been made.
page is the page that the action was made of, the name of the php file
log_name is the name of the action that was done
username is the name of the user that hade made this action
user_id is the id of the user that made this action
ip is the ip adress of the user
2. Create a Class
class log
{
CONST ENVIRONMENT = 'developemnt';
private $id;
protected $log_action;
protected $username;
protected $page;
protected $ip;
protected $log_name;
private $user_id;
public function __construct(string $log_action, string $username, string $log_name)
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
if(!empty($_SESSION['id'])){
$id = $_SESSION['id'];
} else {
$id = 0;
}
$this->log_action = $log_action;
$this->username = $username;
$this->log_name = $log_name;
$this->user_id = $id;
$this->page = basename($_SERVER['PHP_SELF']);
$this->ip = $ip;
}
public function createAction()
{
global $conn;
if(!$conn) {
echo mysqli_error($conn); die;
}
$sql = "INSERT INTO test_log (`log_action`,`username`,`log_name`,`page`,`user_id`,`ip`) values ('".$this->log_action."','".$this->username."','".$this->log_name."','".$this->page."','".$this->user_id."','".$this->ip."')" ;
$sql_query = mysqli_query($conn,$sql);
if(!$sql_query){
echo mysqli_error($conn); die;
}
if(ENVIRONMENT == 'development'){
$_SESSION['msg'] = 'A new log was created ' . $this->log_name;
}
} }
explain:
ENVIRONMENT can be development or production , if it's in development it will show flash messages about the log that has been made
3.Log An Action!
example: log action for login attempts
Create a php file logincheck.php
<?php
session_start();
include("include/configurationadmin.php");
//include_once('../include/classes/config.inc.php');
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];
$sql = mysqli_query($conn,"select * from ".$sufix."admin where username='".$username."'") ;
// HERE HOW TO LOG ACTION
$log = new log("Logging in attempt from $username" , $username ,'Login Attempt' );
$log->createAction();
//SIMPLE AND COOL RIGHT?
if(mysqli_num_rows($sql) > 0)
{
$rows = mysqli_fetch_assoc($sql);
if(md5($password) == $rows['password']) {
$_SESSION['id'] = $rows['id'];
$_SESSION['username'] = $rows['username'];
$_SESSION['usertype'] = $rows['type'];
mysqli_query($conn,"update ".$sufix."admin set lastlogin='".date('Y-m-d')."' where id = '".$rows['id']."' and username='".$rows['username']."'") ;
$domain = ($_SERVER['HTTP_HOST'] != 'localhost') ? $_SERVER['HTTP_HOST'] : false;
setcookie('rrdssrdda', $rows['id'], time()+120, '/', $domain, false);
header("Location: http://localhost/test/admin-new/dashboard");
exit();
} else {
$_SESSION['message']="<div class='alert alert-danger' role='alert'>Invalid userid/password!</div>";
header("Location: http://localhost/test/admin-new/");
exit();
}
} else {
$_SESSION['message']="<div class='alert alert-danger' role='alert'>Invalid userid/password!</div>";
header("Location: http://localhost/test/admin-new/");
exit();
} ?>
Happy Coding!

Categories