3 var path
= require('path'),
4 safe
= require('safetydance'),
6 bcrypt
= require('bcryptjs'),
7 uuid
= require('uuid/v4'),
8 ldapjs
= require('ldapjs'),
9 HttpError
= require('connect-lastmile').HttpError
,
10 HttpSuccess
= require('connect-lastmile').HttpSuccess
,
11 webdavErrors
= require('webdav-server').v2
.Errors
;
13 const LDAP_URL
= process
.env
.CLOUDRON_LDAP_URL
;
14 const LDAP_USERS_BASE_DN
= process
.env
.CLOUDRON_LDAP_USERS_BASE_DN
;
15 const LOCAL_AUTH_FILE
= path
.resolve(process
.env
.LOCAL_AUTH_FILE
|| './.users.json');
16 const TOKENSTORE_FILE
= path
.resolve(process
.env
.TOKENSTORE_FILE
|| './.tokens.json');
17 const AUTH_METHOD
= (LDAP_URL
&& LDAP_USERS_BASE_DN
) ? 'ldap' : 'local';
18 const LOGIN_TOKEN_PREFIX
= 'login-';
19 const API_TOKEN_PREFIX
= 'api-';
21 if (AUTH_METHOD
=== 'ldap') {
22 console
.log('Use ldap auth');
24 console
.log(`Use local auth file ${LOCAL_AUTH_FILE}`);
31 fs
.writeFileSync(TOKENSTORE_FILE
, JSON
.stringify(tokenStore
.data
), 'utf-8');
33 console
.error(`Unable to save tokenstore file at ${TOKENSTORE_FILE}`, e
);
36 get: function (token
, callback
) {
37 callback(tokenStore
.data
[token
] ? null : 'not found', tokenStore
.data
[token
]);
39 getApiTokens: function (callback
) {
40 callback(null, Object
.keys(tokenStore
.data
).filter(function (t
) { return t
.indexOf(API_TOKEN_PREFIX
) === 0; }))
42 set: function (token
, user
, callback
) {
43 tokenStore
.data
[token
] = user
;
47 del: function (token
, callback
) {
48 delete tokenStore
.data
[token
];
54 // load token store data if any
56 console
.log(`Using tokenstore file: ${TOKENSTORE_FILE}`);
57 tokenStore
.data
= JSON
.parse(fs
.readFileSync(TOKENSTORE_FILE
, 'utf-8'));
59 // start with empty token store
62 function verifyUser(username
, password
, callback
) {
63 if (AUTH_METHOD
=== 'ldap') {
64 var ldapClient
= ldapjs
.createClient({ url: process
.env
.CLOUDRON_LDAP_URL
});
65 ldapClient
.on('error', function (error
) {
66 console
.error('LDAP error', error
);
69 ldapClient
.bind(process
.env
.CLOUDRON_LDAP_BIND_DN
, process
.env
.CLOUDRON_LDAP_BIND_PASSWORD
, function (error
) {
70 if (error
) return callback(error
);
72 var filter
= `(|(uid=${username})(mail=${username})(username=${username})(sAMAccountName=${username}))`;
73 ldapClient
.search(process
.env
.CLOUDRON_LDAP_USERS_BASE_DN
, { filter: filter
}, function (error
, result
) {
74 if (error
) return callback(error
);
78 result
.on('searchEntry', function(entry
) { items
.push(entry
.object
); });
79 result
.on('error', callback
);
80 result
.on('end', function (result
) {
81 if (result
.status
!== 0 || items
.length
=== 0) return callback('Invalid credentials');
83 // pick the first found
86 ldapClient
.bind(user
.dn
, password
, function (error
) {
87 if (error
) return callback('Invalid credentials');
89 callback(null, { username: username
});
95 var users
= safe
.JSON
.parse(safe
.fs
.readFileSync(LOCAL_AUTH_FILE
));
96 if (!users
|| !users
[username
]) return callback('Invalid credentials');
98 bcrypt
.compare(password
, users
[username
].passwordHash
, function (error
, valid
) {
99 if (error
|| !valid
) return callback('Invalid credentials');
101 callback(null, { username: username
});
106 exports
.login = function (req
, res
, next
) {
107 verifyUser(req
.body
.username
, req
.body
.password
, function (error
, user
) {
108 if (error
) return next(new HttpError(401, 'Invalid credentials'));
110 var accessToken
= LOGIN_TOKEN_PREFIX
+ uuid();
112 tokenStore
.set(accessToken
, user
, function (error
) {
113 if (error
) return next(new HttpError(500, error
));
115 next(new HttpSuccess(201, { accessToken: accessToken
, user: user
}));
120 exports
.verify = function (req
, res
, next
) {
121 var accessToken
= req
.query
.access_token
|| req
.body
.accessToken
;
123 tokenStore
.get(accessToken
, function (error
, user
) {
124 if (error
) return next(new HttpError(401, 'Invalid Access Token'));
133 exports
.logout = function (req
, res
, next
) {
134 var accessToken
= req
.query
.access_token
|| req
.body
.accessToken
;
136 tokenStore
.del(accessToken
, function (error
) {
137 if (error
) console
.error(error
);
139 next(new HttpSuccess(200, {}));
143 exports
.getProfile = function (req
, res
, next
) {
144 next(new HttpSuccess(200, { username: req
.user
.username
}));
147 exports
.getTokens = function (req
, res
, next
) {
148 tokenStore
.getApiTokens(function (error
, result
) {
149 if (error
) return next(new HttpError(500, error
));
151 next(new HttpSuccess(200, { accessTokens: result
}));
155 exports
.createToken = function (req
, res
, next
) {
156 var accessToken
= API_TOKEN_PREFIX
+ uuid();
158 tokenStore
.set(accessToken
, req
.user
, function (error
) {
159 if (error
) return next(new HttpError(500, error
));
161 next(new HttpSuccess(201, { accessToken: accessToken
}));
165 exports
.delToken = function (req
, res
, next
) {
166 tokenStore
.del(req
.params
.token
, function (error
) {
167 if (error
) console
.error(error
);
169 next(new HttpSuccess(200, {}));
173 // webdav usermanager
174 exports
.WebdavUserManager
= WebdavUserManager
;
176 // This implements the required interface only for the Basic Authentication for webdav-server
177 function WebdavUserManager() {};
179 WebdavUserManager
.prototype.getDefaultUser = function (callback
) {
180 // this is only a dummy user, since we always require authentication
182 username: 'DefaultUser',
184 isAdministrator: false,
192 WebdavUserManager
.prototype.getUserByNamePassword = function (username
, password
, callback
) {
193 verifyUser(username
, password
, function (error
, user
) {
194 if (error
) return callback(webdavErrors
.UserNotFound
);
197 username: user
.username
,
198 isAdministrator: true,
199 isDefaultUser: false,