meteorJS LDAP authentication cannot complete a successful bind - node.js

Hi i am trying to setup LDAP authentication for my meteorJS app and i am following the steps listed in here https://janikvonrotz.ch/2017/02/08/meteor-register-ldap-login-request-handler/
i changed the search filter from mail to username and pushed everything inside of Meteor.startup() here is my code set up
UI code written in /imports/ui/loginform.jsx
let loginUserWithLDAP = (username, password, callback) => {
var loginRequest = {
ldap: true,
username: username,
email: username+"#company.com",
pass: password,
}
Accounts.callLoginMethod({
methodArguments: [loginRequest],
userCallback: callback
})
}
in my /server/ldap.js
Meteor.startup(() => {
var ldapAuth = {
url: 'ldap://company.com:389',
searchOu: 'ou=Employees,ou=\'company Users\', dc=company,dc=com',
searchQuery: (username) => {
return {
filter: '(&(objectclass=user)(samaccountname='+username+'))',
scope: 'sub'
}
}
}
ldapAuth.checkAccount = (options) => {
options = options || {}
ldapAuth.client = ldap.createClient({
url: ldapAuth.url
})
let dn = ['company', 'com']
var future = new Future()
ldapAuth.client.search(
ldapAuth.searchOu,
ldapAuth.searchQuery(options.username),
(error, result)=> {
assert.ifError(error)
result.on('searchEntry', (entry) => {
dn.push(entry.objectName)
return ldapAuth.profile = {
firstname: entry.object.cn,
lastname: entry.object.sn
}
})
result.on('error', function(error){
throw new Meteor.Error(500, "LDAP server error")
})
result.on('end', function(){
if (dn.length === 0) {
future['return'](false)
return false
}
return ldapAuth.client.bind(dn[0], options.pass, (error) =>{
if (error){
future['return'](false)
return false
}
return ldapAuth.client.unbind((error) => {
assert.ifError(error)
return future['return'](!error)
});
})
})
})
return future.wait()
}
Accounts.registerLoginHandler('ldap', (loginRequest)=>{
if (!loginRequest.ldap) {
return undefined
}
if (ldapAuth.checkAccount(loginRequest)){
var userId = null
var user = Meteor.users.findOne({"username": loginRequest.username })
if (!user) {
userId = Accounts.createUser({
username: loginRequest.username,
password: loginRequest.pass,
profile: ldapAuth.profile,
roles: ['user'],
})
Meteor.users.update(userId, { $set: { 'emails.0.verified': true } })
} else {
userId = user._id
}
let stampedToken = Accounts._generateStampedLoginToken()
let hashStampedToken = Accounts._hashStampedToken(stampedToken)
Meteor.users.update(userId,
{ $push: {'services.resume.loginTokens': hashStampedToken } }
)
return {
userId: userId,
token: stampedToken.token
}
}
})
});
In my debugging i found that its erroring out at
result.on('error', function(error){
throw new Meteor.Error(500, "LDAP server error")
})
due to '000004DC: LdapErr: DSID-0C0907E9, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v2580' what does this mean?
What is my code missing?

In short you need to define a search user that does the binding with the LDAP directory.
The post is outdated, I've got you this example: https://github.com/janikvonrotz/Zenkom/blob/0583f01abca96847178a248ff446d84c754965e9/server/actions/ldap.js#L18
Setup the search user like this:
"searchUser": {
"dn": "CN=username,OU=org,DC=company,DC=ch",
"password": "password"
}
The bind user is simply for search the directory. Another bind is executed to authenticate the found user.

Related

createAysncThunk in Redux Toolkit catching error when making fetch request

I'm working on the user registration functionality of an application using Typescript and Redux Toolkit. When I make the fetch request to the signup endpoint, a new user is saved to the database I've connected, but I keep entering the catch error block.
export const registerUser = createAsyncThunk(
"user/registerUser",
async (form: { username:string, password:string }, thunkAPI) => {
try {
const response = await fetch('/api/auth/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userInfo: {
username: form.username,
password: form.password
}
}),
});
if (response.status === 200) return 200;
else return 400;
} catch (e) {
console.log('Error')
}
}
);
I've tried logging the response to the console a number of ways
const data = await response.json() console.log(data)
But have had no luck. I think this is an error with how I've done my fetch request using createAsyncThunk but haven't been able to figure out what I've missed.
This is the code for the initial state and slice:
interface UserState {
userProfile: {
id: number | null;
},
registration: {
status: 'loaded' | 'loading'
}
}
const initialState : UserState = {
userProfile: {
id: null,
},
registration: {
status: 'loaded'
}
};
export const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
},
extraReducers: (builder) => {
builder.addCase(registerUser.fulfilled, (state) => { state.registration.status = 'loaded' }),
builder.addCase(registerUser.rejected, (state) => { state.registration.status = 'loaded' }),
builder.addCase(registerUser.pending, (state) => { state.registration.status = 'loading' })
}
})
And here is the code for the function where the action is dispatched on the UI
const handleRegister= async () => {
if (!username) return alert('Username field was left empty');
if (!password) return alert('Password field was left empty');
const {payload} : any = await dispatch(registerUser({ username: username, password: password}));
if (payload === 200) {
alert('Successfully registered. Redirecting to dashboard');
return navigate('/dashboard');
} else { return alert('User creation unsuccessful'); }
}
Appreciate any help as I've looked through many other posts but haven't been able to resolve my issue.

UnhandledPromiseRejectionWarning: Error: Not Found While sending email from Nestjs

I am trying to develop a mail sending module in my NestJs project. when I send the mail through my local machine it works fine. But when it goes to the server (digital-ocean droplet) it throws the following error and it does not send any email further. what is going on and fix this?
My code segment is below:
Auth controller
#Post('register')
async register(#Body() register: RegisterRequest) {
const res = await this.authService.register(register);
if (res instanceof Error) {
throw res;
}
return {
statusCode: 200,
message:
'Your account is registered successfully. Please check your email to verify your account',
};
}
Auth Service
async register(register: RegisterRequest): Promise<any> {
register.isRegister = true;
register.status = 0;
register.roleId = 5;
const user = await this.userService.save(register);
console.log('User Saved');
if (user instanceof User) {
if (register.isRegister) {
console.log('IS REGISTER');
// Generate Token
const resultsToken = await this.userTokenService.save(user.id, 1);
console.log('USER TOKEN SAVED');
if (resultsToken) {
this.mailService.sendUserConfirmation(user, resultsToken.token);
}
}
}
return user;
}
User Service
async save(register: RegisterRequest): Promise<User> {
try {
const token = generateToken(8);
const user = new User();
user.email = register.email;
user.firstName = register.firstName;
user.lastName = register.lastName;
user.password = hashPassword(register.password || token);
user.role = register.roleId;
user.phoneNumber = register.phoneNumber;
user.isActive = register.status;
const errors = await validate(user);
if (errors.length > 0) {
throw new ParameterMissingException(
errors[0].constraints[Object.keys(errors[0].constraints)[0]],
);
}
const existing = await this.findOneByEmail(register.email);
if (existing) {
throw new EntityFoundException('Email');
}
const registered = await this.userRepository.save(user);
return registered;
} catch (error) {
return error;
}
}
User Token service
async save(userId: number, type: number): Promise<UserToken> {
try {
const userToken = new UserToken();
userToken.user = userId;
userToken.type = type;
userToken.token = generateToken();
if (type === 2) {
userToken.expireIn = 15;
}
console.log('USER TOKEN SAVE');
return await this.userTokenRepository.save(userToken);
} catch (error) {
console.log('USER TOKEN ERROR');
return error;
}
}
Mail Service
sendUserConfirmation(user: any, token: string) {
const url = `${
this.configService.get<string>('REGISTER_CONFIRM_URL') + token
}`;
console.log('MAIL SERVICE ' + url);
this.mailerService
.sendMail({
to: user.email,
// from: '"Support Team" <support#example.com>', // override default from
subject: 'Greetings from CEWAS!',
template: join(__dirname, 'templates') + './register-confirmation',
context: {
url,
},
})
.then((r) => console.log(r))
.catch((err) => {
console.log('MAIL SERVICE ERROR');
console.log(err);
});
}
Mail Module and Config
import { MailerModule } from '#nestjs-modules/mailer';
import { HandlebarsAdapter } from '#nestjs-
modules/mailer/dist/adapters/handlebars.adapter';
import { Module } from '#nestjs/common';
import { MailService } from './mail.service';
import { join } from 'path';
#Module({
imports: [
MailerModule.forRoot({
transport: {
service: 'gmail',
secure: true,
auth: {
user: '********#gmail.com',
pass: '********',
},
},
defaults: {
from: '"<No Reply>" <*******#gmail.com>',
},
template: {
dir: join(__dirname, 'templates'),
adapter: new HandlebarsAdapter(),
options: {
strict: true,
},
},
}),
],
providers: [MailService],
exports: [MailService],
})
export class MailModule {}
Hi try whit this configuration in your transport:
transport: {
host: 'smtp.gmail.com',
port: 465,
ignoreTLS: true,
secure: true,
auth: {
user: '********#gmail.com', //use the .env variable here for security
pass: '********'
},
}
and make sure you turned on access for less secure app
here the link: less secure app

NUXTJS SSR: I have problems with the nuxtServerInit, the req.headers.cookie of the nuxtServerInit is undefined and the cookie is already in my browser

I am new to NuxtJS and have a problem, I am trying to get the token of the user who logged in from the website, the token is stored in the cookie, but, when I start or reload the website page (made with nuxt ssr (universal )), nuxtServerInit should and does start, but req.headers.cookie says it is undefined, so the data cannot be loaded into the store. If I reload the page, the cookie is still in the browser and everything is perfect, the problem is that the req.headers.cookie is: undefined, why? Ahhh, and in development it works perfectly, but in production it doesn't work (in nuxtServerInit the req.headers.cookie is not defined)
I am using Firebase Hosting, Cloud Features, Authentication, Cloud Firestore.
Here is the code:
// store/index.js
import { getUserFromCookie, getUserFromSession } from '../helpers/index.js'
export const actions = {
async nuxtServerInit({ app, dispatch }, { req, beforeNuxtRender }) {
console.log('req.headers.cookie: ' + req.headers.cookie)
console.log('req.session: ', req.session)
if (process.server) {
const user = getUserFromCookie(req)
console.log('process.server: ' + process.server)
console.log('user: ', user)
if (user) {
await dispatch('modules/user/setUSER', {
name: !!user.name ? user.name : '',
email: user.email,
avatar: !!user.picture ? user.picture : '',
uid: user.user_id
})
await dispatch('modules/user/saveUID', user.user_id)
} else {
await dispatch('modules/user/setUSER', null)
await dispatch('modules/user/saveUID', null)
}
}
}
}
// helpers/index.js
import jwtDecode from 'jwt-decode'
var cookieparser = require('cookieparser')
export function getUserFromCookie(req) {
if(req.headers.cookie){
const parsed = cookieparser.parse(req.headers.cookie)
const accessTokenCookie = parsed.access_token
if (!accessTokenCookie) return
const decodedToken = jwtDecode(accessTokenCookie)
if (!decodedToken) return
return decodedToken
}
return null
}
// pages/auth/signup.vue
<script>
import { mapActions } from 'vuex'
export default {
data () {
return {
email: '',
password: '',
renderSource: process.static ? 'static' : (process.server ? 'server' : 'client')
}
},
middleware: ['handle-login-route'],
methods: {
...mapActions('modules/user', [ 'login' ]),
async signUp () {
try {
const firebaseUser = await this.$firebase.auth().createUserWithEmailAndPassword(this.email, this.password)
await this.writeUserData(firebaseUser.user.uid, firebaseUser.user.email)
await this.login(firebaseUser.user)
this.$router.push('/protected')
} catch (error) {
console.log(error.message)
}
},
writeUserData (userId, email) {
const db = this.$firebase.firestore()
return db.collection("Usuarios").doc(userId).set({
email: email
})
}
}
}
</script>
// store/modules/user.js
export const actions = {
async login({ dispatch, state }, user) {
console.log('[STORE ACTIONS] - login')
const token = await this.$firebase.auth().currentUser.getIdToken(true)
const userInfo = {
name: user.displayName,
email: user.email,
avatar: user.photoURL,
uid: user.uid
}
Cookies.set('access_token', token) // saving token in cookie for server rendering
await dispatch('setUSER', userInfo)
await dispatch('saveUID', userInfo.uid)
console.log('[STORE ACTIONS] - in login, response:', status)
},
async logout({ commit, dispatch }) {
console.log('[STORE ACTIONS] - logout')
await this.$firebase.auth().signOut()
Cookies.remove('access_token');
commit('setUSER', null)
commit('saveUID', null)
},
saveUID({ commit }, uid) {
console.log('[STORE ACTIONS] - saveUID')
commit('saveUID', uid)
},
setUSER({ commit }, user) {
commit('setUSER', user)
}
}
Thanks a lot! :D
if you are hosting it on Firebase you should rename your cookie to "__session", that's the only name you could use on Firebase. Their documentation should make it very clear!
Change the following part:
// helpers/index.js
const parsed = cookieparser.parse(req.headers.cookie)
const accessTokenCookie = parsed.__session
// store/modules/user.js
Cookies.set('__session', token)

mtproto/core Telegram replay to another Channel

i want collect news from different channels and echo them in a second channel, with the code i can read channels(not all but most).
I stuck now on the echo problem and have no clue about how i can do this, mtproto is completely new to me, thanks.
Im using the following code i have from another stackoverflow question.
const { MTProto, getSRPParams } = require('#mtproto/core');
const prompts = require('prompts');
const api_id = 'xxxxx';
const api_hash = 'xxxxx';
async function getPhone() {
return (await prompts({
type: 'text',
name: 'phone',
message: 'Enter your phone number:'
})).phone
}
async function getCode() {
// you can implement your code fetching strategy here
return (await prompts({
type: 'text',
name: 'code',
message: 'Enter the code sent:',
})).code
}
async function getPassword() {
return (await prompts({
type: 'text',
name: 'password',
message: 'Enter Password:',
})).password
}
const mtproto = new MTProto({
api_id,
api_hash,
});
function startListener() {
console.log('[+] starting listener')
mtproto.updates.on('updates', ({ updates }) => {
const newChannelMessages = updates.filter((update) => update._ === 'updateNewChannelMessage').map(({ message }) => message) // filter `updateNewChannelMessage` types only and extract the 'message' object
for (const message of newChannelMessages) {
// printing new channel messages
console.log(`[${message.to_id.channel_id}] ${message.message}`)
}
});
}
// checking authentication status
mtproto
.call('users.getFullUser', {
id: {
_: 'inputUserSelf',
},
})
.then(startListener) // means the user is logged in -> so start the listener
.catch(async error => {
// The user is not logged in
console.log('[+] You must log in')
const phone_number = await getPhone()
mtproto.call('auth.sendCode', {
phone_number: phone_number,
settings: {
_: 'codeSettings',
},
})
.catch(error => {
if (error.error_message.includes('_MIGRATE_')) {
const [type, nextDcId] = error.error_message.split('_MIGRATE_');
mtproto.setDefaultDc(+nextDcId);
return sendCode(phone_number);
}
})
.then(async result => {
return mtproto.call('auth.signIn', {
phone_code: await getCode(),
phone_number: phone_number,
phone_code_hash: result.phone_code_hash,
});
})
.catch(error => {
if (error.error_message === 'SESSION_PASSWORD_NEEDED') {
return mtproto.call('account.getPassword').then(async result => {
const { srp_id, current_algo, srp_B } = result;
const { salt1, salt2, g, p } = current_algo;
const { A, M1 } = await getSRPParams({
g,
p,
salt1,
salt2,
gB: srp_B,
password: await getPassword(),
});
return mtproto.call('auth.checkPassword', {
password: {
_: 'inputCheckPasswordSRP',
srp_id,
A,
M1,
},
});
});
}
})
.then(result => {
console.log('[+] successfully authenticated');
// start listener since the user has logged in now
startListener()
});
})

custom user controller method not being called by strapi

So i'm building a site backend for a project using strapi. A requirement for the site backend is there has to be a e-commerce component included since the client wants to do order processing, and process credit card transactions using stripe. With the that being said I decided to write a custom controller for user accounts that are created under the user-permissions plugin for strapi. This way I could link the user accounts from strapi to corresponding customer accounts on stripe's end.
However, i'm running into one problem in particular. The custom user controller methods I have written for creating and updating users seem to work as intended. Though whenever I delete users strapi doesn't seem to use the custom method I have written for deleting users at all. I even included a console.log call but nothing pops up.
I'm not really sure on how to go about fixing this issue since I can't see any of the log calls, and strapi doesn't seem to be spitting out any errors either when this occurs. Any advice on how to go about resolving this issue is appreciated.
Here's the custom controller i'm working on.
<project_dir>/extensions/user-permissions/controllers/User.js
'use strict';
/**
* A set of functions called "actions" for `user`
*/
const _ = require('lodash'),
{ sanitizeEntity } = require('strapi-utils'),
stripe = require('stripe')('<SK_HERE>'),
sanitizeUser = (user) => sanitizeEntity(user, {model: strapi.query('user', 'users-permissions').model}),
formatError = (error) => [{ messages: [{ id: error.id, message: error.message, field: error.field }] }];
module.exports = {
async create(ctx) {
const advanced = await strapi.store({
environment: '',
type: 'plugin',
name: 'users-permissions',
key: 'advanced'
}).get();
const { email, username, firstName, lastName, password, role } = ctx.request.body;
if (!email) return ctx.badRequest('missing.email');
if (!username) return ctx.badRequest('missing.username');
if (!password) return ctx.badRequest('missing.password');
if (!firstName) return ctx.badRequest('missing.firstName');
if (!lastName) return ctx.badRequest('missing.lastName');
const userWithSameUsername = await strapi
.query('user', 'users-permissions')
.findOne({ username });
if (userWithSameUsername) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.username.taken',
message: 'Username already taken.',
field: ['username'],
})
);
}
if (advanced.unique_email) {
const userWithSameEmail = await strapi
.query('user', 'users-permissions')
.findOne({ email: email.toLowerCase() });
if (userWithSameEmail) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.email.taken',
message: 'Email already taken.',
field: ['email'],
})
);
}
}
const user = {
...ctx.request.body,
provider: 'local',
};
user.email = user.email.toLowerCase();
if (!role) {
const defaultRole = await strapi
.query('role', 'users-permissions')
.findOne({ type: advanced.default_role }, []);
user.role = defaultRole.id;
}
try {
const customer = await stripe.customers.create({name: `${firstName} ${lastName}`, email: email});
user.stripeId = customer.id;
const data = await strapi.plugins['users-permissions'].services.user.add(user);
ctx.created(sanitizeUser(data));
} catch (error) {
ctx.badRequest(null, formatError(error));
}
},
async update(ctx) {
const advancedConfigs = await strapi.store({
environment: '',
type: 'plugin',
name: 'users-permissions',
key: 'advanced',
}).get();
const { id } = ctx.params;
const { email, username, password, firstName, lastName} = ctx.request.body;
const user = await strapi.plugins['users-permissions'].services.user.fetch({id});
if (_.has(ctx.request.body, 'email') && !email) {
return ctx.badRequest('email.notNull');
}
if (_.has(ctx.request.body, 'username') && !username) {
return ctx.badRequest('username.notNull');
}
if (_.has(ctx.request.body, 'firstName') && !firstName) {
return ctx.badRequest('firstName.notNull');
}
if (_.has(ctx.request.body, 'lastName') && !lastName) {
return ctx.badRequest('lastName.notNull');
}
if (_.has(ctx.request.body, 'password') && !password && user.provider === 'local') {
return ctx.badRequest('password.notNull');
}
if (_.has(ctx.request.body, 'username')) {
const userWithSameUsername = await strapi
.query('user', 'users-permissions')
.findOne({ username });
if (userWithSameUsername && userWithSameUsername.id != id) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.username.taken',
message: 'username.alreadyTaken.',
field: ['username'],
})
);
}
}
if (_.has(ctx.request.body, 'email') && advancedConfigs.unique_email) {
const userWithSameEmail = await strapi
.query('user', 'users-permissions')
.findOne({ email: email.toLowerCase() });
if (userWithSameEmail && userWithSameEmail.id != id) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.email.taken',
message: 'Email already taken',
field: ['email'],
})
);
}
ctx.request.body.email = ctx.request.body.email.toLowerCase();
}
let updateData = {
...ctx.request.body,
};
if (_.has(ctx.request.body, 'password') && password === user.password) {
delete updateData.password;
}
if(email != null || firstName != null || lastName != null) {
let stripeUpdate = {};
if(email != null && (email !== user.email)) stripeUpdate = {...stripeUpdate, email: email};
if((firstName != null && (firstName !== user.firstName)) || (lastName != null && (lastName !== user.lastName))) stripeUpdate = {
...stripeUpdate,
name: `${firstName != null && (firstName !== user.firstName) ? firstName : user.firstName} ${lastName != null && (lastName !== user.lastName) ? lastName : user.lastName}`
};
if(Object.keys(stripeUpdate).length > 0) {
const customerUpdate = await stripe.customers.update(user.stripeId, stripeUpdate);
}
}
const data = await strapi.plugins['users-permissions'].services.user.edit({ id }, updateData);
ctx.send(sanitizeUser(data));
},
async destroy(ctx) {
console.log('test...');
const { id } = ctx.params,
user = await strapi.plugins['users-permissions'].services.user.fetch({id}),
customerDelete = await stripe.customers.del(user.stripeId),
data = await strapi.plugins['users-permissions'].services.user.remove({id});
ctx.send(sanitizeUser(data));
}
};
Did you activate the destroy action in user-permissions?
I've tested it with this simple User.js in <project_dir>/extensions/user-permissions/controllers/:
module.exports = {
async find(ctx) {
console.log('find!!!');
return [];
},
async destroy(ctx) {
console.log('destroy!!!');
return [];
},
}
console.log('destroy!!!') is called when I send a DELETE request to http://localhost:1337/users/1234

Resources