How to make subsequent requests using mwbot requesting Mediawiki - node.js

I got this error when I make subsequent request using mwbot on node.
response:
{ login:
{ result: 'Aborted',
reason: 'Cannot log in when using MediaWiki\\Session\\BotPasswordSessionProvider sessions' } } }
I am reading pages from mediawiki by providing a title. I thought that every request would need to login to read, but it seemed that I was wrong because this error seemed to complain that I already have logged in. But I don't know how the session can be read or how to find out that I already logged in or not.
the route:
router.get('/wikipage/:title', function(req, res, next) {
let title = req.params.title;
const MWBot = require('mwbot');
const wikiHost = "https://wiki.domain.com";
let bot = new MWBot();
let pageContent = "wiki page not created yet, please create";
bot.login({
apiUrl: wikiHost + "/api.php",
username: "xxx#apiuser",
password: "xxxxx"
}).then((response) => {
console.log("logged in");
return bot.read(title);
}).then((response) => {
for(let prop in response.query.pages) {
pageContent = response.query.pages[prop]['revisions'][0]['*'];
console.log("pageContent:", pageContent);
break;
}
res.json({
data: pageContent
});
}).catch((err) => {
// Could not login
console.log("error", err);
});
});
module.exports = router;

I presume you are running this in a browser, in which case the browser takes care of session cookie handling. You can check it the usual way via document.cookie.

Related

Sending data from expressjs request to form

I have a login system where a user enters their information and when they submit it I validate the info with express and if it is not valid i send an error message. Right now i'm just using res.send for the error message, how would i go about redirecting back to my form but having an error message with it. I would prefer not to use url parameters because that is not secure.
So what I understand, is that you want your login form, that show the error message e.g. the password is wrong.
const login = (req, res) => {
const user = new userModel(req.body.email, req.body.password);
const found = db.findUser(user);
if (found) {
if (user.password == found.password) {
res.status(200).send(true);
} else {
res.status(401).json({ msg: 'The password is incorrect'});
}
} else {
res.status(404).send(false);
}
};
Then you could use the msg property is the password is wrong in the fetch.
fetch('/api/users/login', {
method: 'POST'
})
.then(res => res.json())
.then(data => {
document.getElementById('...some div').innerHTML = `<div>${data.msg}</div>`
})

Sending message from express backend to vuejs frontend

Hi so it's kind of complicated for me, hope anyone can help.
Here's the situation : i have an app divided server side with node/express and front side with Vuejs,
what I'm doing in the back is creating a user here's the code :
const createUser=(req, res, next) => {
console.log("register");
let con=req.con
let { email,password } = req.body;
console.log(req.body)
con.query(
`SELECT * FROM users
WHERE email = $1`,
[email],
(err, results) => {
if (err) {
console.log(err);
res.status(404).json({error: err});
}
console.log(results);
if (results.rows.length > 0) {
//throw new error_types.InfoError("user already exists");
res.status(200).json({error: "user already exists"});
} else {
const hashedPassword = bcrypt.hashSync(password, parseInt(process.env.BCRYPT_ROUNDS));
con.query('INSERT INTO users (email,password) VALUES ($1, $2)',
[email,password],
(err, results) => {
if (err) {
next(err);
}
res.json({info: "User inseted" });
}
);
}
}
);
}
so im checking if it already exists else register it in DB,all good here.
Now in my Vuejs part i have this :
REGISTER({ commit, dispatch, rootState }, { payload }) {
const {email,password} = payload
console.log(payload)
commit('SET_STATE', {
loading: true,
})
const register = mapAuthProviders[rootState.settings.authProvider].register
register(email,password)
.then(success => {
if (success) {
notification.success({
message: "Succesful Registered",
description: "You have successfully registered!",
})
router.push('/auth/login')
commit('SET_STATE', {
loading: false,
})
}
if (!success) {
commit('SET_STATE', {
loading: false,
})
}
})
},
Now the problem happens here as the registration is done all okay but when i use the same email again for another registration it said the same message successfully registred but do not get saved to DB now what i want is the message user aleady exists that appears.
Anyone can help me please?
Edited :added axios part
export async function register(email,password) {
return axios
.post('/register', {emailpassword,})
.then(response => {
if (response) {
const { token } = response.data
if (token) {
store.set('accessToken', token)
}
return response.data
}
return false
})
.catch(err => console.log(err))
}
Without seeing the actual source code that does the HTTP request from the client, it's hard to say exactly what the error handling looks like. The most obvious culprit is this:
res.status(200).json({error: "user already exists"});
You're responding with HTTP 200 OK when an error occurs. Typically, a client implementation will treat this as success. You should signal to clients that an error has occurred - for example, respond with a "409 Conflict". Also make sure the client's fetch() call (or whatever the client uses for talking to the server) does proper error handling (checks statusCode).
The code has another issue, however - a race condition. This is a case of a TOCTTOU (Time-of-Check to Time-of-Use), where a non-zero amount of time passes between the existence check (SELECT) and the INSERT. If two users are registering for the same e-mail at the same time, they could both get a success.
You should remove the check altogether and use uniqueness constraints offered by the database instead (UNIQUE INDEX). This way, the DB guarantees there can be no duplicates and you don't have to worry about race conditions.

How to communicate from the back end to the front end in a MEAN stack application?

I'm new to the MEAN stack app and am having some trouble trying to send data from the server to the front end. However, I do have some communication going on, but this is all I can seem to do. In the server I have the json message being sent.
Server
router.route("/users/register").post((req, res) => {
registerLogin.findOne({ $or: [{ username }, { email }]}, (err, user) => {
if (err)
res.send(err);
else if (!username || !email || !password)
res.status(403).json({ registerErrRequired: "Fill out whole form." });
Front end
registerUser(username, email, password) {
const user = {
username: username,
email: email,
password: password
};
return this.http.post(`${this.uri}/users/register`, user)
.pipe(map(response => console.log(response)))
.subscribe(() => { this.router.navigate(["/users/login"]) }, (errResp) => {
errResp.error ? this.ngFlashMessageService.showFlashMessage({
messages: [errResp.error.registerErrRequired], // Where json message gets caught and shown to the browser
dismissible: true,
timeout: false,
type: 'danger'
}) : console.log("An unkown error occured.");
});
}
This works well, but I can't seem to do req/res other than using a flash message. My issue is wanting to use it in other ways than just flash messages. For example, if the user does not have a session, then I want them to navigate back to the the log in page. Here's what I tried but failed.
Server
// Middleware
const redirectLogin = ((req, res, next) => {
if (!req.session.user)
res.status(401).json({ loggedIn: false });
else
next();
});
// Route
router.route("/home").get(redirectLogin, (req, res) => {
Blog.find((err, docs) => {
if (err)
console.log(err);
else
res.json(docs);
});
});
Front end
homePage() {
// Here is where I would like to say, If session, then true, else navigate(["/users/login"])
if (loggedIn === false)
this.router.navigate(["/users/login"])
else
// Success
return this.http.get(`${this.uri}/home`);
}
The only way I found communication was through sending error flash messages, but nothing else.
What you can do is call an api to check whether the user is logged in or not in ngOnInit lifecycle hook,so every time your component loads you can check whether the session exists on backend and route accordingly.
export class App implements OnInit{
constructor(){
//called first time before the ngOnInit()
}
ngOnInit(){
//CheckLogin() is a method in your service which calls your backend api
this.http.post("your authentication url to check if session exits ",{username:username}).subscribe(data=>{
if (data["loggedIn"] === false)
this.router.navigate(["/users/login"])
})
}
}
Angular also has HTTP interceptors,you can solve this issue with jwt and http interceptors

Serverside NodeJS - need client windows ID

(intranet application) I'm using ReactJS with NodeJS as a server side api for accessing various resources (such as user active directory profile). I need to integrate with Active Directory, without prompting the user for their credentials.
Using ActiveDirectory for Node (npm i activedirectory) I can query the AD using LDAP for a hard coded sAMAccountName using the code below.
ad.findUser(sAMAccountName, function(err, user) {
if (err) {
console.log('ERROR: ' + JSON.stringify(err));
}
if (!user) console.log('User: ' + sAMAccountName + ' not found.');
else {
thisUser = user;
}
});
But what I cant figure out, is how to pick up the current user ID when they call the API.
These 2 examples give the 'Server' user id, rather then the client ID:
const user = process.env;
const user = require('os').userInfo().username;
In .net I would normally do this with
string NTID = HttpContext.Current.User.Identity.Name;
So is there a way in NodeJS, running on a server, to access the client user ID?
---------UPDATE:-----------
I've been trying to implement the solution below using passport-windowsauth but cant quite get it working. I have the web.config file configured like this:
<iisnode watchedFiles="*.js;node_modules\*;routes\*.js;views\*.jade" promoteServerVars="LOGON_USER,AUTH_USER" />
<security>
<authentication>
<anonymousAuthentication enabled="true" />
<windowsAuthentication enabled="true" />
</authentication>
</security>
Azure configuration is set to:
This is the function I call when I click a button to start the authentication:
activeDirectory = () => {
let url = '';
//Default to current user when in dev
if (
window.location.href.indexOf('localhost') > -1
) {
url = 'http://localhost:5000/express-passport';
} else {
url = '/express-passport';
}
axios({
method: "GET",
url: url,
withCredentials: true
})
.then(response => {
console.log("Done: ", response.data);
})
.catch(error => {
console.log("An error occurred - ", error);
});
};
And this is the NodeJs server route code:
const passport = require("passport");
const WindowsStrategy = require("passport-windowsauth");
module.exports = app => {
let thisUser = {};
passport.use(
new WindowsStrategy(
{
ldap: {
url: "ldap://somethingldap.somewhere.com/CN=,DC=,DC=,DC=,DC=",
base: "CN=,DC=,DC=,DC=,DC=",
bindDN: "serviceAccountDetailsHere",
bindCredentials: "masked"
}
},
function(profile, done) {
thisUser = profile;
console.log("Profile:", profile);
}
)
);
app.get(
"/express-passport",
function(req, res, next) {
passport.authenticate("WindowsAuthentication", function(
error,
user,
info
) {
// log everything to console
console.log(error);
console.log(user);
console.log(info);
if (error) {
res.status(401).send(error);
} else if (!user) {
res.status(401).send(info);
} else {
next();
}
res.status(401).send(info);
})(req, res);
},
// function to call once successfully authenticated
function(req, res) {
res.status(200).send("logged in!");
}
);
};
So now, when the page opens I click a button, expecting it to authenticate. A popup appears asking for my credentials, which I enter, but the popup just disappears and reappears continuously. I'm obviously doing something incorrect...
Your biggest problem will be getting this working on your MacOS dev machine in a way that can be seamlessly transferred to a Windows machine and work the same way.
The node-sspi package is probably easiest, but it's also Windows-only.
I think your best bet would be to use the passport-windowsauth module for Passport.js and use it with LDAP, since you can make LDAP queries to AD from any OS.
On the server, put it behind IIS and follow their instructions under Integrated Authentication to setup IIS with Windows Authentication to make it a seamless login for users.
On your MacOS dev machine, you can't do that of course. I think the best you can do for MacOS is that it will ask you for credentials. You will have to test this out. I'm not sure if it will do authentication challenge without IIS like it normally would with (I don't think it will). If not, you can create a separate login page by following their Non-integrated authentication instructions and you only use that on your dev machine.
In the actual call to passport.use(), the only difference between the two is integrated: false. So you may find you will have to set that differently for local dev vs. the server (hopefully you can have your code detect if you're on your dev machine and set it to false automatically).
So yes, you'll have different login behaviour between your dev machine and the server, but at least you should be able to promote your code as-is and have it work in both places.
Below is the solution I ended up using for this. It's a combination of 2 packages -
express-ntlm
and
ActiveDirectory
ntlm is used to pick up the user credentials from the browser (some users may have to provide those credentials if prompted). Once ntlm has userID, I use that to query the active directory to get more details for the user. The reason for 2 modules is that ntlm only provides 3 fields - UserName, DomainName and Workstation. In my case, I needed email address, first name, last name etc.
https://www.npmjs.com/package/express-ntlm
https://www.npmjs.com/package/activedirectory
const ntlm = require('express-ntlm');
const ActiveDirectory = require('activedirectory');
module.exports = app => {
const config = {
url: 'ldap://something.somewhere.com',
baseDN: 'CN=a,DC=b,DC=c',
username: '',
password: ''
};
app.use(
//This is for getting the users MS ID only
ntlm()
);
app.get(‘/apiName/’, (req, res) => {
let sAMAccountName = req.ntlm.UserName;
const ad = new ActiveDirectory(config);
var userDetails = {
Email: '',
FirstName: '',
LastName: ''
};
ad.findUser(sAMAccountName, (err, user) => {
if (err) {
console.log('ERROR: ' + JSON.stringify(err));
}
if (!user) console.log('User: ' + sAMAccountName + ' not found.');
else {
userDetails.Email = user.mail;
userDetails.FirstName = user.firstName;
userDetails.LastName = user.lastName;
}
res.json(userDetails);
res.end();
});
});
});
};
To answer your question as you mentioned above But what I can't figure out, is how to pick up the current user ID when they call the API.
It seems the above code is wrapped under function which is used as route handler callback fn. by saying this I mean you will definitely have access to req and res aka request and response API function argument.
for example:-
module.exports = {
routeHanlder: function(req, res) {
ad.findUser(sAMAccountName, function(err, user) {
if (err) {
console.log('ERROR: ' + JSON.stringify(err));
}
if (!user) console.log('User: ' + sAMAccountName + ' not found.');
else {
thisUser = user;
}
});
}
}
so in above example, the req argument contains all the client information which you want like from where the request comes, what are the request headers and so on.
as you mentioned in your question that string NTID = HttpContext.Current.User.Identity.Name; by using HttpContext you can get user identity. By using req you will get client information.

sails session writing bug

I'm using sails 0.10.4 and stumbled with one pretty annoying bug. When user logs in I write his data into the req.session.user then in policies I can retrieve his data such as his role, password etc. But the req.session.user becomes undefined when I go out of the login action. Do you have any ideas how to handle this? Here's the code:
api/controllers/User.js :
module.exports = {
login: function (req, res) {
Users.findOneByEmail(req.param('email'))
.exec(function (err, user) {
if ((err) || (!user)) {
res.send({
error: 'User not found'
});
return;
}
if (!passwordHash.verify(req.param('password'), user.password)) {
res.send({
error: 'Incorrect passwpord'
});
return;
}
req.session.user = user;//I write user into the session
res.send({
user: user
});
});
}
}
api/policies/isLoggedIn.js
module.exports = function (req, res, next) {
if (req.headers.authentication) {
var credentials = JSON.parse(req.headers.authentication);
if(req.session.user.login === credentials.login)//User doesn't exist in session
return next();
}
}
In a testing environment , this issue can happen when testing with Supertest and not defining an agent
var agent = request.agent(app);
agent.post('/api/login',{email:'foo#bar.com',password:'foobar})
.end(function(err,res){...; done();});
It is the correct way to work with sessions, simply using request.post would not work as it would reinit the session variable as soon as the response is sent, even if we are chaining requests inside the same test.
Learnt it the hard way, so I hope it can help some lost developper.

Resources