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('Using ldap auth');
24 console
.log(`Using local auth file at: ${LOCAL_AUTH_FILE}`);
33 fs
.writeFileSync(TOKENSTORE_FILE
, JSON
.stringify(tokenStore
.data
), 'utf-8');
35 console
.error(`Unable to save tokenstore file at ${TOKENSTORE_FILE}`, e
);
38 get: function (token
, callback
) {
39 callback(tokenStore
.data
[token
] ? null : 'not found', tokenStore
.data
[token
]);
41 getApiTokens: function (callback
) {
42 callback(null, Object
.keys(tokenStore
.data
).filter(function (t
) { return t
.indexOf(API_TOKEN_PREFIX
) === 0; }))
44 set: function (token
, user
, callback
) {
45 tokenStore
.data
[token
] = user
;
49 del: function (token
, callback
) {
50 delete tokenStore
.data
[token
];
56 // load token store data if any
58 console
.log(`Using tokenstore file at: ${TOKENSTORE_FILE}`);
59 tokenStore
.data
= JSON
.parse(fs
.readFileSync(TOKENSTORE_FILE
, 'utf-8'));
61 // start with empty token store
64 function verifyUser(username
, password
, callback
) {
65 if (AUTH_METHOD
=== 'ldap') {
66 var ldapClient
= ldapjs
.createClient({ url: process
.env
.CLOUDRON_LDAP_URL
});
67 ldapClient
.on('error', function (error
) {
68 console
.error('LDAP error', error
);
71 ldapClient
.bind(process
.env
.CLOUDRON_LDAP_BIND_DN
, process
.env
.CLOUDRON_LDAP_BIND_PASSWORD
, function (error
) {
72 if (error
) return callback(error
);
74 var filter
= process
.env
.CLOUDRON_LDAP_FILTER
.replace(/\{username\}/g, username
);
75 ldapClient
.search(process
.env
.CLOUDRON_LDAP_USERS_BASE_DN
, { filter: filter
, scope: "sub" }, function (error
, result
) {
76 if (error
) return callback(error
);
80 result
.on('searchEntry', function(entry
) { items
.push(entry
.object
); });
81 result
.on('error', callback
);
82 result
.on('end', function (result
) {
83 if (result
.status
!== 0 || items
.length
=== 0) return callback('Invalid credentials');
85 // pick the first found
88 ldapClient
.bind(user
.dn
, password
, function (error
) {
89 if (error
) return callback('Invalid credentials');
91 callback(null, { username: username
});
97 var users
= safe
.JSON
.parse(safe
.fs
.readFileSync(LOCAL_AUTH_FILE
));
98 if (!users
|| !users
[username
]) return callback('Invalid credentials');
100 bcrypt
.compare(password
, users
[username
].passwordHash
, function (error
, valid
) {
101 if (error
|| !valid
) return callback('Invalid credentials');
103 callback(null, { username: username
});
108 exports
.init = function (config
) {
112 exports
.login = function (req
, res
, next
) {
113 verifyUser(req
.body
.username
, req
.body
.password
, function (error
, user
) {
114 if (error
) return next(new HttpError(401, 'Invalid credentials'));
116 var accessToken
= LOGIN_TOKEN_PREFIX
+ uuid();
118 tokenStore
.set(accessToken
, user
, function (error
) {
119 if (error
) return next(new HttpError(500, error
));
121 next(new HttpSuccess(201, { accessToken: accessToken
, user: user
}));
126 exports
.verify = function (req
, res
, next
) {
127 var accessToken
= req
.query
.access_token
|| req
.body
.accessToken
;
129 tokenStore
.get(accessToken
, function (error
, user
) {
130 if (error
) return next(new HttpError(401, 'Invalid Access Token'));
139 exports
.verifyIfNeeded = function (req
, res
, next
) {
140 if (!gConfig
.folderListingEnabled
) return exports
.verify(req
, res
, next
);
144 exports
.logout = function (req
, res
, next
) {
145 var accessToken
= req
.query
.access_token
|| req
.body
.accessToken
;
147 tokenStore
.del(accessToken
, function (error
) {
148 if (error
) console
.error(error
);
150 next(new HttpSuccess(200, {}));
154 exports
.getProfile = function (req
, res
, next
) {
155 next(new HttpSuccess(200, { username: req
.user
.username
}));
158 exports
.getTokens = function (req
, res
, next
) {
159 tokenStore
.getApiTokens(function (error
, result
) {
160 if (error
) return next(new HttpError(500, error
));
162 next(new HttpSuccess(200, { accessTokens: result
}));
166 exports
.createToken = function (req
, res
, next
) {
167 var accessToken
= API_TOKEN_PREFIX
+ uuid();
169 tokenStore
.set(accessToken
, req
.user
, function (error
) {
170 if (error
) return next(new HttpError(500, error
));
172 next(new HttpSuccess(201, { accessToken: accessToken
}));
176 exports
.delToken = function (req
, res
, next
) {
177 tokenStore
.del(req
.params
.token
, function (error
) {
178 if (error
) console
.error(error
);
180 next(new HttpSuccess(200, {}));
184 // webdav usermanager
185 exports
.WebdavUserManager
= WebdavUserManager
;
187 // This implements the required interface only for the Basic Authentication for webdav-server
188 function WebdavUserManager() {};
190 WebdavUserManager
.prototype.getDefaultUser = function (callback
) {
191 // this is only a dummy user, since we always require authentication
193 username: 'DefaultUser',
195 isAdministrator: false,
203 WebdavUserManager
.prototype.getUserByNamePassword = function (username
, password
, callback
) {
204 verifyUser(username
, password
, function (error
, user
) {
205 if (error
) return callback(webdavErrors
.UserNotFound
);
208 username: user
.username
,
209 isAdministrator: true,
210 isDefaultUser: false,