Dual Base URL in Moodle - moodle

I have installed the XAMPP and Moodle 2.5 inside it.
Along side, I've also installed No-ip client to convert this to a webserver.
Now my question is, I woud like to have this server reacheble from my intranet and from outside.
My config.php file have base url like:
$CFG->wwwroot = 'http://172.16.1.1/exam';
$CFG->dataroot = 'H:\\xampp\\moodledata';
$CFG->admin = 'admin';
But not limited to local ip address, I want to use it as webserver to access it from internet. Please suggest me how to set the base url to use it from internet and also from my intranet.

The simplest solution is use a domain name which can be accessed from both internet and internal machines.
Failing that, the Moodle config file is simply a PHP file so you can do something like this:
if (access_via_internet()) {
$CFG->wwwroot = WEBROOT_FOR_INTERNET_ACCESS;
} else if (access_via_intranet()) {
$CFG->wwwroot = WEBROOT_FOR_INTRANET_ACCESS;
} else {
throw new Exception("Ye cannae change the laws of physics");
}
function access_via_internet() {
// Do something to detect access via the internet.
// Probably parse $_SERVER['HTTP_HOST']
return (did_we_detect_the_internet() ? true : false);
}
function access_via_intranet() {
// Do something to detect access via the intranet.
// Probably just negate the value of access_via_internet()
return !access_via_internet();
}
Another option is to just rely on the server address by which the user came to you:
$CFG->wwwroot = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].'/exam';
One potential issue, if you do allow access from different addresses: do not copy and paste Moodle URLs into course content. It's easy to forget, but the URLS will only work for users also accessing from the same route.

Related

Dynamic Links from a CMS - Error: "redirect" can not be returned from getStaticProps during prerendering

I have a Next JS app connected to a CMS and hosted on Vercel - all links are dynamic and the pages are created by the content authors.
I am trying to create dynamic redirects that will force URLs to adhere to formats that are better for SEO. For example:
Enforce lowercase URLs
Replace spaces with dashes
Remove trailing slashes
For example, /test/Author Name/ would redirect to /test/author-name
Since I need to trigger a 301 redirect for these wrong URLs, the only way to do this with Next JS from what I have found is to return a Redirect from getStaticProps, this is what I have so far:
export const getStaticProps: GetStaticProps = async (context) => {
let requestedUrl = '/';
if (context?.params?.path) {
requestedUrl = '/' + (context?.params?.path as string[]).join('/');
}
//check for URLs with uppercases, spaces, etc. and clean them up
let modifiedUrl = requestedUrl;
modifiedUrl = modifiedUrl.trim().toLowerCase().replace(/\s\s+/g, ' ').replace(/\s/g, '-');
if (modifiedUrl != requestedUrl) {
return {
redirect: {
destination: modifiedUrl,
permanent: true,
},
};
}
This works wonderfully well running locally and connected to the CMS - everything is working as it should and all "faulty" URLs are corrected with the correct response code.
Sadly, this does not work on build, I have spent so much time so far trying to find an alternative, but no matter what I do, the build on Vercel fails with the error:
"redirect" can not be returned from getStaticProps during prerendering
The next best potential solution is to use Middleware, but that requires v.12 at least. Due to limitations from the CMS connector, we are forced to use Node v.11 :(
The alternative that I have built is to use router.push on the client side, but this... just looks terrible. The page loads, returns a 200, and then loads again with the corrected URL. Not good for the user's experience.
Any advice or suggestions? I am baffled that something this simple is this complicated with Next JS!
I resolved the issue... it looks like redirects on statically generated pages are not possible unfortunately. I removed getStaticProps and getStaticPaths, and added getServerSideProps instead. The redirects are now working correctly, but the site is not as fast as we are losing out on SSG.

PWA multiple virtual paths with same backend code does not create separate installs

I have a generic common NodeJS app that multiple users access. The users are identified via the path. For example: https://someapp.web.app/abc can be one path while https://someapp.web.app/def can be another path.
On the NodeJS server path, I send the same server code by passing the path parameters to the program. The route appears something like this:
app.get('/*', async (req, res) => {
...
locals.path = req.path;
...
res.render('index', locals);
}
In the above index is a template that uses locals data for customisation
What I would like is that for each path there is a separate manifest and its associated icons and that on a single device (phone or desktop) multiple installations be possible. Thus, https://someapp.web.app/abc be one icon and https://someapp.web.app/def be another icon.
I am having difficulty in the placement and the scoping of the manifest and service worker. It always adds only one icon (the first path installed) to the home screen or desktop. My settings are:
In the public (root) folder I have each manifest viz. abc-manifest.json and def-manifest.json and a common sw.js.
The abc-manifest.json is:
'scope': '/abc',
'start_url': '/abc',
...
The access to the service-worker from the index.js is:
if (navigator.serviceWorker) {
navigator.serviceWorker.register('sw.js')
.then(function (registration) {
console.log('ServiceWorker registration succeeded');
}).catch(function (error) {
console.log('ServiceWorker registration failed:', error);
});
}
I have tried changing the paths of scope and start_url to / but it did not work. Since all requests to the public path are common and not within the virtual /abc path, I am unable to figure out how to get this working.
Thanks
Could that be an option to have a dedicated route that will redirect the user to /abc or /def?
In the manifest:
{
"start_url": "https://example.com/login",
"scope": "https://example.com/",
}
/login would make sure to redirect to /abc or /def.
This way you could keep one service worker, and one manifest.
And in the Service Worker, maybe try to return the specific icon based on file name.
self.addEventListener('fetch', e => {
// Serve correct icon
let url = new URL(e.request.url)
if (url.pathname.contains('/android-icon-512.png')) {
return respondWith(e, '/android-icon-512-abc.png')
}
// other ifs…
// Return from cache or fallback to network.
respondWith(e, e.request)
})
const respondWith = (e, url) =>
e.respondWith(caches.match(url)
.then(response => response || fetch(e.request).then(response => response))
)
Maybe you’ll need a specific header to do this, or use a URL parameter (icon.png?user=abc) to help query the right icon. I’m throwing idea, because it probably depends a lot on your app back-end and/or front-end architecture.
I once did this: the back-end (PHP / Laravel) handled the correct returning of the icon and manifest (I had one for each use case) based on other stuff.

Meteor - Password recovery / Email confirmation dynamic url

Basically, I'm using the accounts-base package on meteor and on meteor startup, I set up what template the server should use for the password recovery mail, email confirmation mail, etc.
For example, in my server/startup.js on meteor startup I do many things like :
Accounts.urls.verifyEmail = function (token) {
return Meteor.absoluteUrl(`verify-email/${token}`);
};
Accounts.emailTemplates.verifyEmail.html = function (user, url) {
return EmailService.render.email_verification(user, url);
};
The problem is that my app is hosted on multiple host names like company1.domain.com, company2.domain.com, company3.domain.com and if a client wants to reset his password from company1.domain.com, the recovery url provided should be company1.domain.com/recovery.
If another client tried to connect on company2.domain.com, then the recovery url should be company2.domain.com.
From my understanding, this is not really achievable because the method used by the Accounts Package is "Meteor.absoluteUrl()", which returns the server ROOT_URL variable (a single one for the server).
On the client-side, I do many things based on the window.location.href but I cannot seem, when trying to reset a password or when trying to confirm an email address, to send this url to the server.
I'm trying to find a way to dynamically generate the url depending on the host where the client is making the request from, but since the url is generated server-side, I cannot find an elegent way to do so. I'm thinking I could probably call a meteor server method right before trying to reset a password or create an account and dynamically set the ROOT_URL variable there, but that seems unsafe and risky because two people could easily try to reset in the same timeframe and potentially screw things up, or people could abuse it.
Isn't there any way to tell the server, from the client side, that the URL I want generated for the current email has to be the client current's location ? I would love to be able to override some functions from the account-base meteor package and achieve something like :
Accounts.urls.verifyEmail = function (token, clientHost) {
return `${clientHost}/verify-email/${token}`;
};
Accounts.emailTemplates.verifyEmail.html = function (user, url) {
return EmailService.render.email_verification(user, url);
};
But I'm not sure if that's possible, I don't have any real experience when it comes to overriding "behind the scene" functionalities from base packages, I like everything about what is happening EXCEPT that the url generated is always the same.
Okay so I managed to find a way to achieve what I was looking for, it's a bit hack-ish, but hey..
Basically, useraccounts has a feature where any hidden input in the register at-form will be added to the user profile. So I add an hidden field to store the user current location.
AccountsTemplates.addField({
_id: 'signup_location',
type: 'hidden',
});
When the template is rendered, I fill in this hidden input with jQuery.
Template.Register.onRendered(() => {
this.$('#at-field-signup_location').val(window.location.href);
});
And then, when I'm actually sending the emailVerification email, I can look up this value if it is available.
Accounts.urls.verifyEmail = function (token) {
return Meteor.absoluteUrl(`verify-email/${token}`);
};
Accounts.emailTemplates.verifyEmail.html = function (user, url) {
const signupLocation = user.profile.signup_location;
if (signupLocation) {
let newUrl = url.substring(url.indexOf('verify-email'));
newUrl = `${signupLocation}/${newUrl}`;
return EmailService.render.email_verification(user, newUrl);
}
return EmailService.render.email_verification(user, url);
};
So this fixes it for the signUp flow, I may use the a similar concept for resetPassword and resendVerificationUrl since the signupLocation is now in the user profile.
You should probably keep an array of every subdomains in your settings and keep the id of the corresponding one in the user profile, so if your domain changes in the future then the reference will still valid and consistent.

Authentication That Doesn't Require Javascript?

I have a Web API app, initialized thusly:
app.UseCookieAuthentication();
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOAuthBearerTokens(OAuthOptions);
app.UseGoogleAuthentication();
For calls to most controllers, it works great. However, it also requires a bit of javascript before client-side service calls are made:
function getSecurityHeaders() {
var accessToken = sessionStorage["accessToken"] || localStorage["accessToken"];
if (accessToken) {
return { "Authorization": "Bearer " + accessToken };
}
return {};
}
The problem is that we have a certain type of controller (one that accesses files) where no javascript can be run during the call. For example, the call might be to:
http://mysite/mycontroller/file/filename.jpg
...where the value is assigned as the src attribute of an img tag. The call works, but Thread.CurrentPrincipal.Identity is unauthenticated with a null name, so there's currently not a way to enforce security.
I'm new to Web API, so it may be a dumb question, but what's the way around this? What switches do I need to flip to not require javascript to add security headers? I was considering trying to find a way to force an authorization header in an IAuthorizationFilter or something, but I'm not even sure that would work.
So I figured out the solution to my problem.
First, I needed to configure the app to use an authentication type of external cookies thusly:
//the line below is the one I needed to change
app.UseCookieAuthentication(AuthenticationType = DefaultAuthenticationTypes.ExternalCookie);
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOAuthBearerTokens(OAuthOptions);
app.UseGoogleAuthentication();
Second, it turned out there was a line of code in my WebApiConfig file that was disabling reading the external cookie:
//this line needed to be removed
//config.SuppressDefaultHostAuthentication();
After that, I could see the external cookie from Google, which passed along an email address I could identify the user with.

Knowing from which udp socket the Radius request came. Using FreeRadius

I know the question isn't very well. Sorry my english.
I want to setup a (one instance of) FreeRadius server to listen to several ports (with a bunch of 'listen' sections) and then pass the that udp port as a parameter along with User-Name and User-Password to a script that I want to use to make the authentication.
The basic idea is make some kind of domain separation. Some Firewall use radius port 2000 to make authentication. Some other different firewall (with a different set of users) use radius port 2020, for example. At the end, all the request fall in the same script that has the knowledge of both set of users and use one or the other according to the given extra attribute (port number)
I know that is possible making a virtual server per 'domain'. but I prefer not to replicate configuration files. and i think is shorter to add a little 'listen' section for every domain I want.
I tried to add an atribute this way:
listen {
ipaddr = *
port = 0
type = auth
update control {
Login-TCP-Port = 1812
}
}
and tried to read it:
autorize {
if ("%{User-Name}" == "bob") {
update reply {
Reply-Message = "This is only %{Login-TCP-Port} an example."
}
update control {
Cleartext-Password := "bob"
}
ok
}
[...]
}
But don't work.
How can i make it right?
Is this posible?
Hope you can help me.
I'm answering myself. I found (looking a like further on google) that the Packet-Dst-Port attribute have the data that I want.
I get it from here (now that I found it, look pretty obvious :P)