Restrict access to file in web folder in Yii 2?

Hi,
I am using Yii 2 basic application template. I have a file data.php in web folder. This file contains the database results. Now in js folder I have app.js where app.js calls data.php to use the database results in order for displaying chart. Everything works very correctly.

Now on my subdomain when the user logs in as admin he can find the chart correctly. The requirement is that data.php should not be able to show its contents on the browser. If the user logs in to the application , then application.example.com/index.php?r=dashboard/index page is displayed which shows the chart.

If the user enters application.example.com/data.php, he will see the database results fetched that he should’nt see.

I have created .htaccess file in basic/web folder where there is data.php file.
In that file if I enter

Options -Indexes

still the user can access the data.php

If I enter below code in .htaccess

<Files ~ "^.*">
  Deny from all
</Files>

<Files ~ "^index\.php|css|js|.*\.png|.*\.jpg|.*\.gif">
  Allow from all
</Files>

then the user can’t access the data.php but graph is not displayed.

How to accomplish?

Here is my data.php

<?php
header('Access-Control-Allow-Origin: *');

header('Access-Control-Allow-Credentials: true');
header('Content-Type: application/json');

$servername = "example.com";
$username = "";
$password = "";
$dbname = "ex_shgrecords";


// Create connection
$conn = new mysqli($servername, $username, $password,$dbname);

// Check connection
if ($conn->connect_error) {
    //die("Connection failed: " . $conn->connect_error);
} 
//echo "Connected successfully";



$sql = "SELECT CONCAT(employee.FirstName,' ',employee.LastName) as FullName, count(*) as TotalGroups from groupdetails, employee WHERE groupdetails.EmpId=employee.EmpId group by groupdetails.EmpId";

$result = $conn->query($sql);

if ($result!=null) {
    // output data of each row
   $data = array();
	foreach ($result as $row) {
	$data[] = $row;
	}
} 
$conn->close();

print json_encode($data);

Below is app.js which uses data.php




$(document).ready(function(){
	$.ajax({
		url: "data.php",
		method: "GET",
			dataType: "json",
		success: function(data) {
			console.log(data);
			var emp = [];
			var groups = [];

			for(var i in data) {
				emp.push(" " +data[i].FullName);
				groups.push(data[i].TotalGroups);
			}

			var chartdata = {
				labels: emp,
				datasets : [
					{
						label: 'SHG Groups',
						backgroundColor: 'skyblue',
						borderWidth:1,
						data: groups
					}
				]
			};

			var ctx = document.getElementById("groupschart");

			var barGraph = new Chart(ctx, {
				type: 'bar',
				data: chartdata,
				options: {
        scales: {
            yAxes: [{
                ticks: {
                    beginAtZero:true,
				    stepSize: 1
					
                },

         
				
            }],
				xAxes: [{
            barPercentage: 0.4
        }]
        },
        title: {
            display: true,
            text: 'Representation of Total Groups for each Field Officer',
            fontSize:15,
            fontColor:"black",
            fontStyle:"bold"
        }

    }
			});
		},
		error: function(data) {
			console.log(data);
		}
	});
});

Is there any other way by which I could accomplish above.

As long as you are accessing “data.php” via ajax like this, you have to put “data.php” file under the web accessible folder and allow everyone to access it. If you deny accessing it, ajax also will fail.

So, why don’t you make a normal action method in the controller which responds to the ajax call? In which you can do what you are doing in data.php. And you can restrict access to the action using ACF or RBAC.

I would highly recommend what softark said about using more yii idiomatic approach by creating an action.

alternative approach would be to check for yii session in your data.php file something like

if (!isset($_SESSION['session_name'])) {
}

note: you have to set the session and delete on login and logout

Placing the code of data.php in a controller action is the best way to do it

Where should I place app.js file? Currently it is in basic/web/js . Now should I make a controller and view for data.php? Where should I place data.php code in view or in action of controller. How to accomplish? can you please tell me step by step.

I have dashboard/index where I am showing charts. So should I use the same controller. If so how to go ahead.

I moved the file data.php and placed it in views/dashboard. In Dashboard Controller, I have action actionData from where I use $this->render(‘data’).

If I have below code in app.js then still graph is not displayed

$(document).ready(function(){
	$.ajax({
		url: "index.php?r=dashboard/data.php",
		method: "GET",
			dataType: "json",
		success: function(data) {
			console.log(data);
			var emp = [];
			var groups = [];

			for(var i in data) {
				emp.push(" " +data[i].FullName);
				groups.push(data[i].TotalGroups);
			}

			var chartdata = {
				labels: emp,
				datasets : [
					{
						label: 'SHG Groups',
						backgroundColor: 'skyblue',
						borderWidth:1,
						data: groups
					}
				]
			};

			var ctx = document.getElementById("chart");

			var barGraph = new Chart(ctx, {
				type: 'bar',
				data: chartdata,
				options: {
        scales: {
            yAxes: [{
                ticks: {
                    beginAtZero:true,
				    stepSize: 1
					
                },

         
				
            }],
				xAxes: [{
            barPercentage: 0.4
        }]
        },
        title: {
            display: true,
            text: 'Representation of Total Groups for each Field Officer',
            fontSize:15,
            fontColor:"black",
            fontStyle:"bold"
        }

    }
			});
		},
		error: function(data) {
			console.log(data);
		}
	});
});

You can put it where it is now.

You may add a new action to an existing controller. I don’t think you have to create a new controller.
Also I don’t think you have to create a view script, because you only need to send back the query result in json format.

https://www.yiiframework.com/doc/guide/2.0/en/runtime-responses

Is the db connection used here the same one in your Yii app? If so, you can (and should) do this query using DAO(Database Access Object) (https://www.yiiframework.com/doc/guide/2.0/en/db-dao).

And if you already have ActiveRecord classes for employee and groupdetails tables, then you may want to do it using ActiveRecord (https://www.yiiframework.com/doc/guide/2.0/en/db-active-record).

It should be "index.php?r=dashboard/data"

Hi,

I have placed my graph.js in basic/web/js. After making changes, the url is

url: "index.php?r=dashboard/data",

Also in Dashboard controller, I have written an action as below:

public function actionData()
    {
        
        $sql = Yii::$app->db->createCommand('SELECT CONCAT(employee.FirstName," ",employee.LastName) as FullName, count(*) as TotalGroups from groupdetails, employee WHERE groupdetails.EmpId=employee.EmpId group by groupdetails.EmpId')
            ->queryAll();
            
        if ($sql!=null) {
    
   $data = array();
	foreach ($sql as $row) {
	$data[] = $row;
	}
} 

print json_encode($data);
            
    }
    

When I try to see that data is correctly displayed, then data is displayed in json format along there is error as below:

[{"FullName":"Silas Makasare","TotalGroups":"3"},{"FullName":"Nick Johnson","TotalGroups":"4"},{"FullName":"Joshuva William","TotalGroups":"2"},{"FullName":"Ashish Bansode","TotalGroups":"2"},{"FullName":"Samuel Kamble","TotalGroups":"5"},{"FullName":"Caleb Sathe","TotalGroups":"4"}]

An Error occurred while handling another error: yii\web\HeadersAlreadySentException: Headers already sent in /home/ifsoftware1234/public_html/application/basic/controllers/DashboardController.php on line 69. in /home/ifsoftware1234/public_html/application/basic/vendor/yiisoft/yii2/web/Response.php:366 Stack trace: #0 /home/ifsoftware1234/public_html/application/basic/vendor/yiisoft/yii2/web/Response.php(339): yii\web\Response-&gt;sendHeaders() #1 /home/ifsoftware1234/public_html/application/basic/vendor/yiisoft/yii2/web/ErrorHandler.php(135): yii\web\Response-&gt;send() #2 /home/ifsoftware1234/public_html/application/basic/vendor/yiisoft/yii2/base/ErrorHandler.php(111): yii\web\ErrorHandler-&gt;renderException(Object(yii\web\HeadersAlreadySentException)) #3 [internal function]: yii\base\ErrorHandler-&gt;handleException(Object(yii\web\HeadersAlreadySentException)) #4 {main} Previous exception: yii\web\HeadersAlreadySentException: Headers already sent in /home/ifsoftware1234/public_html/application/basic/controllers/DashboardController.php on line 69. in /home/ifsoftware1234/public_html/application/basic/vendor/yiisoft/yii2/web/Response.php:366 Stack trace: #0 /home/ifsoftware1234/public_html/application/basic/vendor/yiisoft/yii2/web/Response.php(339): yii\web\Response-&gt;sendHeaders() #1 /home/ifsoftware1234/public_html/application/basic/vendor/yiisoft/yii2/base/Application.php(392): yii\web\Response-&gt;send() #2 /home/ifsoftware1234/public_html/application/basic/web/index.php(12): yii\base\Application-&gt;run() #3 {main}

And now when I view dashboard/index, graph is not displayed.

Would you please try this?

public function actionData()
{
    \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    $rows = Yii::$app->db->createCommand( ... )->queryAll();
    return json_encode($rows);
}

You have to return the response content, not to print or echo it.

Check the following section of the Guide: Guide > Responses > Response Body

Hi,

I only used return instead of print. It works fine.

Thanks