joomla forbid direct access to components and modules - joomla2.5

I activated SEO friendly URLs. Basically URLs in my app looks like following:
http://x.com/en or http://x.com/en/gallery.
From my app there is no link, let's say, on com_users. But user still can open it with one of the following URLs: http://x.com/component/users or http://x.com/?option=com_banners.
I blocked first one with this:
RewriteCond %{REQUEST_URI} /component/ [NC]
RewriteRule ^.*$ - [F,L]
How can I block the second (?option=com_users)?
I understand that this behavior could be default and expected for Joomla, but I just want to give you one example.
When I allowed access to all my pages for only registered users they still are able to access components. At the same time in Joomla administration there is no permission for read. Finally, users are getting template page or some data if it is public, for ex., articles from com_content. And question: how to raise 403 in this case or, at least, redirect to / ?
Update:
I need to block /users?view=registration, reset remind and profile. And I need to redirect any error to login page. Doesn't matter whether it is whole Joomla component or view, task etc.

I would go another way, and use rel=canonical for this.
This is a much easier/better way of doing things, as the tag will appear on all "Page Versions" and you don't need to set many case-specific rules or carry around a heave redirect file...
This is just one Plugin that will help your canoniczlization.
http://extensions.joomla.org/extensions/site-management/seo-a-metadata/meta-data/11038?qh=YTo0OntpOjA7czo5OiJjYW5vbmljYWwiO2k6MTtzOjExOiInY2Fub25pY2FsJyI7aToyO3M6MTI6IidjYW5vbmljYWwnLiI7aTozO3M6NToiY2Fub24iO30%3D

I wrote my own plugin to handle all cases and redirect to login page (/login) in case of any inconvenience. By inconvenience I mean any direct access to any component in Joomla, or 403, or 404, but not 500. For now, my application works very well accepting only following URLs: /login, /home, /gallery, /gallery/album/any, and few others. Direct access is totally forbidden, though, user cannot use URL params (like ?option=com_users) or /component/ path.
This approach wouldn't work with SEO URLs turned off.
<?php // no direct access
defined( '_JEXEC' ) or die( 'Restricted access' );
jimport( 'joomla.event.plugin' );
class plgSystemComontrol extends JPlugin {
function plgSystemComcontrol(& $subject, $config) {
parent::__construct($subject, $config);
}
function onAfterRoute() {
// get plugin parameters
$com_redirect_url = $this->params->def('com_redirect_url', 'index.php?option=com_user&view=login');
$com_debug = $this->params->def('com_debug', '0');
$com_message = $this->params->def('com_message', '');
// get option, view, task ..
$mainframe = JFactory::getApplication();
$option = JRequest::getCmd('option');
$view = JRequest::getCmd('view');
$task = JRequest::getCmd('task');
// get current URL
$uri = JFactory::getURI();
$url = $uri->toString();
$u_host = $uri->getHost();
$u_path = $uri->getPath();
$path = substr($url, strlen(JURI::root()));
// get user permissions
$groupsUserIsIn = JAccess::getGroupsByUser(JFactory::getUser()->id);
$user_type = implode(" ",$groupsUserIsIn);
$group_sum = array_sum($groupsUserIsIn);
if ($com_debug == '1') {
$mainframe->enqueueMessage('--------------------------------');
$mainframe->enqueueMessage('$option = '.$option);
$mainframe->enqueueMessage('$view = '.$view);
$mainframe->enqueueMessage('$task = '.$task);
$mainframe->enqueueMessage('$url = '.$url);
$mainframe->enqueueMessage('$path = '.$path);
}
if (strpos($path, 'administrator') === 0) {
return;
}
// set default redirect page
$redirectPage = ($group_sum > 1) ? 'index.php' : 'index.php/login';
$directAccess = strpos($path, 'component') !== false || strpos($path, 'option') !== false;
// allow login page only
if ($option == 'com_users') {
if (($view == 'login' || empty($view) || $task == 'user.login' || $task == 'user.logout') && !$directAccess) { // $view == 'default'
return;
} else {
$mainframe->redirect($redirectPage, $directAccess ? 'Direct access to components forbidden' : 'Login/logout is enabled only');
//JError::raiseError(403, JText::_('Forbidden'));
//return;
}
}
// deny direct access to components
if ($directAccess) {
$mainframe->redirect($redirectPage, 'Direct access to components forbidden');
//JError::raiseError(401, JText::_('/component/'));
}
// get usertype to see if logged-in
// $user =& JFactory::getUser();
// $user_type = $user->get('usertype');
$groupsUserIsIn = JAccess::getGroupsByUser(JFactory::getUser()->id);
$user_type = implode(" ",$groupsUserIsIn);
$group_sum = array_sum($groupsUserIsIn);
if ($group_sum > '1') {
return ;
}
//if user logged-in, then return from function
if (empty($option)) {
return;
}
$mainframe->redirect( $com_redirect_url, $com_message );
return;
}
}
?>
I hope this will help to understand how to do some custom redirects and disable direct access to the components.

Related

Varnish: How to change language of the site based on a cookie?

I have my site almost working. It works perfect with one language, but I have a cookie that sets the language. I hashed it also.
The problem is that I cant change the value of my cookie, I cant get an idea about how to do that.
My site receives a variable called "lg=1" where "1" is the language code.
I dont get the idea about how to pass that to my site, to get the "english" version and save the new cookie (with lg=1 value) again, so next time the user access without the lg=1 variable, he visits our english site, based on cookie value.
Can someody help me?
Thanky you
If you want to be able to set a cookie based on get-parameter you have two options
Set the cookie with javascript. Here is a SO answer for setting Cookie with JS
Configure Varnish to always pass requests containing "lg=" to your application so that you can set the cookie there.
sub vcl_recv {
if (req.url ~ ".*lg=") {
return (pass);
}
#Your other code in vcl_recv.....
}
You will probably need to edit your VCL file, by default something like /etc/varnish/default.vcl
When you receive a language parameter, you can set a cookie via Varnish.
Then use the value of the cookie to override the request url to the backend.
It should be something like that :
sub vcl_recv {
// Use the value of the cookie to override the request url.
if (req.http.Cookie ~ "lg") {
set req.url = req.url + "?" + req.http.Cookie;
}
// Go to VCL_error to redirect and remove the parametter.
if (req.url ~ "(?i)lg=1") {
error 802 "Remember to use the english version";
}
}
sub vcl_error {
/* Removes lg parameter and remember the english choice */
if (obj.status == 802) {
set obj.http.Set-Cookie = "lg=1; domain=." + req.http.host + "; path=/";
set obj.http.Location = req.http.X-Forwarded-Proto + "://" + req.http.host + regsub(req.url, "\?lg=1", "");
set obj.status = 302;
return(deliver);
}
}
I would prefer to use a different subdomain for each language, like that you can just redirect the user to the subdomain when you receive a parameter. By doing that you would not need to override the request.url each time.
I will use below approach to achieve:
Check lg cookie is exist or not. If not exist then set it using back-end server or varnish server.
I preferred to set language cookie using varnish instead of back-end server to avoid excessive request/load on back-end server.
Default language selection must be English i.e. "1"
Set the hash based on user language selection.It will help to maintain different cache based on language also retrieve the data from cache.
Please refer below code:
I have used IPD cookie.
backend default {
#applicable code goes here
}
sub identify_cookie{
#Call cookie based detection method in vcl_recv
if (req.http.cookie ~ "IPD=") {
set req.http.Language = regsub(req.http.cookie, "(.*?)(IPD=)([^;]*)(.*)$", "\3");
}
}
C{
#used to set persistent(9+ years) cookie from varnish server.
const char* persistent_cookie(char *cookie_name){
time_t rawtime;
struct tm *info;
char c_time_string[80];
rawtime = time(NULL) * 1.2; /*Added 9 years*/;
info = localtime(&rawtime);
strftime(c_time_string,80,"%a, %d-%b-%Y %H:%M:%S %Z", info);
char * new_str ;
if((new_str = malloc(strlen(cookie_name)+strlen(c_time_string)+1)) != NULL){
new_str[0] = '\0'; // ensures the memory is an empty string
strcat(new_str,cookie_name);
strcat(new_str,c_time_string);
} else {
syslog(LOG_INFO,"Persistent cookie malloc failed!\n");
}
return new_str;
}
}C
sub vcl_recv {
call identify_cookie; #Used to get identify cookie and get its value
if(!req.http.Cookie ~ "IPD"){
C{
VRT_SetHdr(sp, HDR_REQ, "\006X-IPD:",persistent_cookie("IPD=1; domain=yourdomain.com; path=/; Expires="),vrt_magic_string_end);
}C
}
}
sub vcl_fetch {
set beresp.http.Set-Cookie = req.http.X-IPD; #used for debug purpose only.Check cookie in response header
}
sub vcl_backend_response {
#applicable code goes here
}
sub vcl_deliver {
#applicable code goes here
set resp.http.X-Cookie = "Cookies Set ("req.http.X-IPD")"; #used for debug purpose only.Check cookie in response header
}
sub vcl_error {
#applicable code goes here
}
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
}
if(!req.http.Language){
req.http.Language = 1 #default english language
}
hash_data(req.http.Language); # make different hash for different language to avoid cache clashing
return(hash);
}
sub vcl_pipe {
#applicable code goes here
}
sub vcl_pass {
#applicable code goes here
}
Note: Please ensure to change cookie name only.Regular expression supports to retrive cookie value from multiple cookies without considering its order

Sitecore Url works with any Extension

In my website , the url's are working even entering something after .aspx and giving staus code 200.
Eg: below is normal page with .aspx and status code 200.
But even i have any random extension i got 200 status code which suppossed to be a 404 Status code,
Any Help.
Sitecore is quite generous when it resolves URL's. If you want to enforce correct extensions, you could create a custom Item Resolver which ensures the context item remains null in the process method if the URL has the incorrect extension.
Here's a helpful article on creating an Item Resolver:
Thoughts on httpRequestBegin - Custom Item Lookups
In my example below, the base process method is called. After that we check if the Context Item meets the requirements, and set it to null if not. (You'll need to implement TemplateIsAPageType and ExtensionIsValid as you see fit.)
public class CustomItemResolver : HttpRequestProcessor
{
public override void Process( HttpRequestArgs args )
{
base.Process(args);
if( Context.Item != null && TemplateIsAPageType() && !ExtensionIsValid())
{
Context.Item = null;
}
}
}
Another approach might be something like this, where we compare the requested URL with the resolved item's 'ideal' URL:
public class CustomItemResolver : HttpRequestProcessor
{
public override void Process( HttpRequestArgs args )
{
base.Process(args);
if( Context.Item == null)
return;
var requestUrl = HttpContext.Current.Request.RawUrl;
var idealUrl = LinkManager.GetItemUrl(Context.Item);
if(requestUrl != idealUrl)
Context.Item = null;
}
}
Sitecore skips everything after the last dot ”.” in an url when attempting to resolve an item.
This is done by the class Sitecore.Web.RequestUrl which has a property called ItemPath. This property attempts to create a valid path to an item from the requested url. It is not possible to override this property.
If you for some reason would like Sitecore to return a 404 status code if an item is requested with a file extension, such as .aspx, you could do something like this in a 404 not found item resolver. See this post http://laubplusco.net/handling-404-sitecore-avoid-302-redirects/ the following method extends the one shown in the post.
protected virtual bool IsValidContextItemResolved(string filePath)
{
if (Context.Item == null || !Context.Item.HasContextLanguage())
return false;
if (filePath.Contains(".") && !RequestIsForPhysicalFile(filePath))
return false;
return !(Context.Item.Visualization.Layout == null
&& string.IsNullOrEmpty(WebUtil.GetQueryString("sc_layout")));
}
It is important to ensure that the requested url is not for a physical file first. This is done by checking that the args.Url.filepath does not map to a physical file.
The rule which I show here says that if an item has been resolved and the filepath contains a dot then the requested url should return a 404 and the context item should be the not found item. The code could be extended to check what comes after the dot to see if it is a valid extension.

Query Facebook Opengraph next page parameters

I am unable to implement pagination with Facebook OpenGraph. I have exhausted every option I have found.
My hope is to query for 500 listens repeatedly until there are none left. However, I am only able to receive a response from my first query. Below is my current code, but I have tried setting the parameters to different amounts rather than having the fields from the [page][next] dictate them
$q_param['limit'] = 500;
$next_exists = true;
while($next_exists){
$music = $facebook->api('/me/music.listens','GET', $q_param);
$music_data = array_merge($music_data, $music['data']);
if($music["paging"]["next"]==null || $music["paging"]["next"]=="")
$next_exists = false;
else{
$url = $music["paging"]["next"];
parse_str(parse_url($url, PHP_URL_QUERY), $array);
foreach ($array as $key => $value) {
$q_param[$key]=$value;
}
}
}
}
a - Can you please share what do you get after first call?
b - Also, possible if you can share the whole file?
I think your script is timing out. Try adding following on top of your file:
set_time_limit(0);
Can you check apache log files?
sudo tail -f /var/log/apache2/error.log

Handling 404 error Joomla 2.5

I try to redirect all my 404 error on my web site following those docs :
http://docs.joomla.org/Creating_a_Custom_404_Error_Page
So I edited my error.php :
<?php
defined('_JEXEC') or die;
if (($this->error->getCode()) == '404') {
header('Location: http://www.mywebsite.com');
exit;
}
if { (!isset($this->error)) {
$this->error = JError::raiseWarning(404, JText::_('JERROR_ALERTNOAUTHOR'));
$this->debug = false;
}
//get language and direction
$doc = JFactory::getDocument();
$this->language = $doc->language;
$this->direction = $doc->direction;
?>
But when I clic on a link that should redirect me to a 404 error page - which should now redirect to my home page, it goes to the following link instead :
http://www.mywebsite.com/index.php?Itemid=359
How may I solve that problem ?
Joomla needs an itemID when building a page. My guess is that your default page is item ID 359. Joomla is appending that to the URL most likely because you do not have SEF URLs with rewrite turned on.
Correct code that you need (and more general) is:
if (($this->error->getCode()) == '404') {
header('Location: /index.php');
exit;
}
Some suggested the below, but I just want to redirect to main page not to an article:
if (($this->error->getCode()) == '404') {
header('Location: /index.php?option=com_content&view=article&id=999');
exit;
}

Login only prestashop catalog

Im building a prestashop catalog, but it needs to be visible to logged in customers only. Is this possible. It would be nice if the built in prestashop login is used for this.. any help is appreciated.
I have a suggestion. You can use the Customer Groups feature in PrestaShop 1.5 and only allow logged in customers to see the prices. For every Customer that is grouped in Visitor, they would see your website in Catalog Mode.
Prestashop 1.5 solution:
Simply upload the original file:
classes\controller\FrontController.php
into:
override/classes/controller/FrontController.php
Next, rename the class. Final code should look like this:
class FrontController extends FrontControllerCore
{
public function init()
{
parent::init();
if (!$this->context->customer->isLogged() && $this->php_self != 'authentication' && $this->php_self != 'password')
{
Tools::redirect('index.php?controller=authentication?back=my-account');
}
}
}
The last step is to manually delete the following file so prestashop is aware of the overriden class (It will be re-generated automatically):
cache/class_index.php
And voilà, functionality achieved without overwriting core files.
It'll be easy.
Use this code:
if(!self::$cookie->isLogged(true) AND in_array($this->step, array(1, 2, 3)))
Tools::redirect('authentication.php');
In the preprocess of your indexController
Here’s my solution, it works like a charm and is a very easy fix!
In classes\Configuration.php (around line 114) it looks like this
static public function get($key, $id_lang = NULL)
{
if ($id_lang AND isset(self::$_CONF_LANG[(int)$id_lang][$key]))
return self::$_CONF_LANG[(int)$id_lang][$key];
elseif (is_array(self::$_CONF) AND key_exists($key, self::$_CONF))
return self::$_CONF[$key];
return false;
}
change it to this:
static public function get($key, $id_lang = NULL)
{
//Grab access to the $cookie which is already loaded in the FrontController as global $cookie;
global $cookie;
if ($id_lang AND isset(self::$_CONF_LANG[(int)$id_lang][$key]))
return self::$_CONF_LANG[(int)$id_lang][$key];
elseif (is_array(self::$_CONF) AND key_exists($key, self::$_CONF))
//If the system is trying to find out if Catalog Mode is ON, then return the configuration setting,
//but override it with the user logon status
if($key == 'PS_CATALOG_MODE')
{
return !$cookie->logged || self::$_CONF[$key];
}
else
{
return self::$_CONF[$key];
}
return false;
}
Essentially, I wanted to force the system to display the “Catalog Mode” when the user is not logged in, and to turn this off when he is logged in.
I can guarantee this works for v1.4.3.0 and the code for the current version 1.4.8.2 (at the time of this post) has not changed, so it should work there.