User Record Ownership with OctoberCMS & MySQL - php

I'm using OctoberCMS based on Laravel, with the official User plugin.
I'm making a gallery where frontend users can upload files.
The user is the owner of their uploaded file and is the only one that has permission to edit.
My question, is this the correct way to design and handle user record ownership? All user upload records will be mixed together in one database table mysite_gallery_ and will be sorted and displayed in html with filters, such as viewing all uploads by a specific username.
Will this be slow? Should each user have their own table? Will it be secure enough to prevent another user, bot, or hack script from editing a file record they don't own?
MySQL Table
All upload records are saved to the table mysite_gallery_.
| id | username | filename | slug | title | tags |
| ---- | ---------- | ---------- | -------- | --------- | -------------------- |
| 1 | matt | xyz123 | xyz123 | My File | space, galaxy, stars |
Record Ownership
At upload, my custom Upload component uses Laravel to create a record in the database of the file's title, slug, tags, etc.
To define ownership I have the Upload component save the user's username to the record.
# Get Current User
$user = '';
if (Auth::check()) {
$user = Auth::getUser();
$user = $user->username;
}
# Create Record
$gallery = new Gallery();
$gallery->username = $user;
$gallery->filename = $name;
$gallery->title = $title;
$gallery->slug = $slug;
$gallery->tags = $tags;
$gallery->save();
Edit Record
If the user wants to edit the file properties, such as title, Laravel checks if current user matches the username in the record. If user is owner, it allows edit.
# Get File Record Owner
$owner = '';
if (Gallery::where('filename', '=', $filename)->exists()) {
$record = Gallery::where('filename', '=', $filename)->first();
$owner = $record->username;
}
# Authenticate Current User is Owner
$is_owner = false;
if (Auth::check()) {
# Get Current User
$user = Auth::getUser();
# Check if User is Owner
if ($user->username == $owner) {
$is_owner = true;
}
}
# Edit Record
if ($is_owner == true) {
# Update Record
Gallery::where('filename', '=', $filename)->update(['title' => $title]);
return Redirect::back();
}

It is a better idea to use the users id instead of the username. It'll take less space in the database and is also faster.
Also I would put the tags in another table. Although this depends on how you use the tags. If you have them in another table then it would be easier to get all the uploads for a tag for example.

Related

PHP delete image related to post ID

Okay so I have an ajax request that leads to a file called inc/ajax/del_images.php which delete's the image a user selected to delete
Edit_post.php:
<form class="form-control" action="" autocomplete="off" method="post" enctype="multipart/form-data">
<img id="img" src="some url from database">
<button id="delete-img" data-id="W12kwd2">Delete img</button>
<img id="img" src="some url from database">
<button id="delete-img" data-id="T93pm3P">Delete img</button>
</form>
data-id is the id of the img in the database table
Also images and buttons and gen from php which i didnt include as it adds no value to post
Ajax on edit_post:
$("#delete-img").on('click', function() {
$.ajax({
url: 'inc/ajax/del_images.php',
type: "POST",
data: {
img_id: $(this).attr("data-id")
},
cache: false,
success: function(result){
console.log(result);
}
});
)};
Then in del_images.php:
session_start();
if(isset($_POST['img_id'])){
//image id
$iid = $_POST['img_id'];
//let's check if this image id is valid/in the database
require("conn_user.php");
$stmt = $conn->prepare("SELECT * FROM `images` WHERE `ID` = ?");
$stmt->bind_param("s", $iid);
$stmt->execute();
$stmt_results = $stmt->get_result(); // get result
$row_get = $stmt_results->fetch_assoc();
if($stmt_results->num_rows > 0){
//img with the id was found
//now check if the current user is the owner of post with post[ID] related to the image[ID]
$stmt = $conn->prepare("SELECT * FROM `posts` WHERE `ID` = ?");
$stmt->bind_param("s", $row_get['post_id']);
$stmt->execute();
$stmt_results = $stmt->get_result(); // get result
$row_get_post = $stmt_results->fetch_assoc();
if($stmt_results->num_rows > 0){
//post was found lets check $_SESSION with poster id(in DB)
if($_SESSION['uid'] == $row_get_post['poster_id']){
//this means the current user is the owner of post aswell as the image
//now delete the image cuz the user is the owner which means its safe
$stmt = $conn->prepare("DELETE FROM `images` WHERE ID = ?");
$stmt->bind_param("s", $iid);
$stmt->execute();
$delete_results = $stmt->store_results(); // get result
if($delete_results->affected_rows == 1){
//image was deleted return info so page
print_r('image deleted!');
}else{
print_r('image could not be deleted!');
}
}else{
//id didnt match prop a hacker so force kick and admin review
//code removed for this post
}
}else{ //post not found this will never happen but if it does just add error output }
}else{
//img not found please tell the user
//this code was removed for simplicity of the post
}
}
MY DATABASES :
images table
| ID | post_ID | url |
| :--------:| :--------:|:--------:|
| W12kwd2 | 1 | mNDNJD3324kmWD382n3r.png |
| T93pm3P | 1 | In3u2n329dnjJDEJKDde.jpg |
| Wo90dmp | 2 | JNMduwio3232ndsakdew.jpeg|
posts table
| ID | post_title | poster_id |
| :--------: | :--------: |:--------: |
| 1| What a title | 1 |
| 2| Can you code?| 1|
| 3| Ajax, why and how | 4 |
MY ISSUE :
The issue
So another user can't delete another users image cuz i am check that they are the owner of the post of which the image is related too but lets say the user is busy editing post 1 the edit post url will look like this edit_post?post_id=1 which is fine but the user can in the buttons data-id insert the id of images related to post ID 2 and delete them cuz he is the owner of post ID 2 aswell(you can see it from db example) now first i think lets just get the id from the url but any idiot who knows how frontend works will be able to check the js to just insert the value they want for the url id= so how can i limit this so that a user can only delete the images of the post that they are currently editing without having to work with a frontend supply id
i tough maybe to use a $_SESSION['current_edit'] = "current id of post which they clicked edit on" but the issue leads what is they have multi tabs cuz they editing more that one post I know i need to work with some type of supplied id but how can i lock it down so that users can't delete images of other posts they own while editing another post.
FOOTER NOTE*
if I need to supply more info and edit the post to be more clear of more specific please tell me and i will do it as i know StackOverflow is a clean and well maintained site ~ Have a great day :)
delete image from folder PHP
This post may help you.
What you need to do.
query the id from database with ajax
then fetch the url column.
Delete the file by unlinking the url line you called to whatever your file system is.
That's all the process.

how to send mail using swiftmailer - Yii2

I tried to send email using swiftmailer in Yii2. I'm still beginner for this framework. This is my basic code:
public function sendMail($email)
{
if ($this->validate()) {
$email = Yii::$app->params['adminEmail'];
$mailto = 'user#gmail.com'; // need to change
$isi = "blablabla";
Yii::$app->mailer->compose("#app/mail/layouts/html", ["content" => $isi])
->setTo($mailto)
->setFrom($email)
->setSubject('coba')
->send();
return true;
}
return false;
}
In my case, I want to setup a 'setTo' based on my usr table. Field of my usr table:
id | username | manager | email | type |
1 | fauzi | arie | fauzi#gmail.com | user |
2 | arie | rian | arie#gmail.com | approver |
For example, when user with id = 1 login then he's create a new post and after click submit, the action also send email to user with id = 2 (manager of fauzi) for some review before post is publish to public. Thank you.
note: I assume your usr table has model called User if not, then change it to fit your code
you can provide also array of email adresses
like
->setTo(['email1#email.com', 'email1#email.com' => 'Name Surname'])
What you want to know is which users you want to get, maybe add some conditions to activerecord etc...
$users = User::find()->all();
or
// or any condition you need in your application, this is just example
$users = User::find()->andWhere(['type' => 'approver'])->all();
then Variant #1 you can loop trough all records
foreach ($users as $user) {
// you can add here some conditions for different types of users etc
// ... your code to send email ...
->setTo([$user->email => $user->username])
// or just
// ->setTo($user->email)
}
or Variant #2 you can get all emails first
$emails = \yii\helpers\ArrayHelper::getColumn($users, 'email');
// ... your code to send email ...
->setTo($emails)
// ...

Check if the user_id is exist inside query

I've tried to query using laravel eloquent where user is following a specific brand.
The problem: How to query listing of brand and let me know if current login user is following the brand?
got 3 tables:
Brand:
Id | name | logo
User:
Id | name | email | password
Followers:
brand_id | user_id
Now i tried to query all brand and inside of the collection i want to add
is_follow = 1 or is_follow = 0
if the user already follow or 0 if not exists.
I'm using fractal so maybe it can be easier to query. but i don't really get it how to query it out with check the user_id first.
Thanks
*Update
I manage to solve it. But i think its a bad practice.
$user_id = Request::get('user_id');
foreach($brands->followers as $value){
$array[] = $value->user_id;
}
if(in_array($user_id, $array)){
$is_follow = 1;
}
You can check if the authenticated User follows a specific Brand with:
$user = Auth::user();
$exists = $user->brands->contains($brand_id);
You can also do it with a raw query which will be better in terms of performance:
$exists = DB::table('user_brand')
->whereBrandId($brand_id)
->whereUserId(Auth::user()->id)
->count() > 0;

Issue in displaying data that should just be visible for a particular ID not to all

I am trying to display records of a particular job that has already been done by someone else before a new provider sees it. If the status is open, there should not be any information to be displayed as supposedly, no one has made any report about it. If the status is awarded, then necessary data should be displayed. Right now, the information to be shown are viewable. The problem the data is displayed in every job post even it is not the report for such a job.
Example,
Job ID | Title | Description | Subject | Job Status
2 | Math Tutor | I need Math tutor! I need Math tuto... | Mathematics | Open
1 | English Tutor | Edited... | French | Awarded
If I click "Open", I should not be able to see any record because it is still not done. If I click "Awarded", I should see details about the job. Right now, the data is showing properly for JOB ID 1 which was already awarded. However, the same data is shown as well in JOB ID 2.
How do I properly display the data in its proper place? I've been trying everything to do it. I included the JOB ID to be displayed to see if there's something wrong with it. But there's none, it shows JOB ID 1 in both jobs 1 and 2. How do I display it just in job 1 where it belongs?
Here's my code in controller:
public function view_tutors_tutorials()
{
$this->validateRole('provider');
$this->load->model('tutorial_model');
$this->load->model('auth_model');
$data['subject_list'] = $this->array_to_select( $this->tutorial_model->get_all_subjects(), 'id','name');
$my_preference = $this->tutorial_model->get_tutors_tutorials(isset($_GET['subject_id'])?$_GET['subject_id']:'0', isset($_GET['sort_by'])?$_GET['sort_by']:'');
$data['my_preference'] = $my_preference;
$this->load->view('provider/view_tutors_tutorials', $data);
}
and this in my model:
public function get_tutors_tutorials($subject_id = NULL, $sort_by = NULL)
{
//responsible for displaying job contracts for provider user.
$this->db->select('tutorial.status as status, tutorial.client_id as client_id, tutorial.id as tutorial_id, subject.name as name, tutorial.title as title, tutorial.description as description, tutorial.start_date as start_date, tutorial.update_date_time as update_date_time,tutorial_proposal.provider_id as provider_id,provider.first_name as first_name,provider.last_name as last_name,tutorial.contract_status as contract_status,tutorial.provider_feedback as provider_feedback,tutorial.client_notetoself as client_notetoself,tutorial.client_feedback as client_feedback,tutorial.provider_notetoself as provider_notetoself,tutorial.material_used,tutorial.recommendation')->from('tutorial');
$this->db->join('subject', 'subject.id = tutorial.subject_id');
$this->db->join('tutorial_proposal', 'tutorial_proposal.provider_id = tutorial.provider_id');
$this->db->join('provider', 'provider.id = tutorial_proposal.provider_id');
$this->db->where('tutorial.status', 'Awarded');
if ( ! empty($subject_id) )
{
$this->db->where('subject_id', $subject_id);
}
//if there's no sort selection made, the jobs will be sorted from newest to oldest
if ( empty($sort_by))
{
$sort_by = "update_date_time desc";
}
$this->db->order_by($sort_by);
$query = $this->db->get();
return $query->result_array();
}
I look forward to getting any help.
You need to remove where condition for status in you model's query :
$this->db->where('tutorial.status', 'Awarded'); // this should be removed
Also make sure, your subject_id passed properly.

create tree through recursive function

I have a table
id,name,parent_id,designation columns,
i want create tree through recursive function in php.
every parent_id is looking in id column and if user login then user can see own and all below records according parent_id.
like
A
|
B
|
c
|
D
|
E
|
F
if A user login then he can all(A,B,C,D,E,F) details.and if B login then see (B,c,D,E,F) and like all... if F login then he can see only own records..
Thanks for advance
create a function fetch_parent;
function fetch_parent($parent_id) {
$query = 'SELECT * FROM `my_table` WHERE `parent_id`='. $parent_id;
// use your own sql class/function whatever to retrieve the record and store it in variable $parent
if($parent->parent_id !== null) { // asuming a 'root' record will have null as it's parent id
fetch_parent($parent->parent_id); // here you go with your recursion
}
return;
}
Then just call the function with the record you want it's parents from:
$first_parent_id = 8;
fetch_parent($first_parent_id);
Notes:
the $parent var can also be an array, depending on the mysql result set
PLEASE PLEASE PLEASE check $parent_id in the query for mysql injection etc.

Categories