new login system

This commit is contained in:
Massimo Melina 2020-05-12 12:14:41 +02:00
parent b681fd506e
commit 4c4200ab68
5 changed files with 230 additions and 159 deletions

File diff suppressed because one or more lines are too long

141
main.pas
View File

@ -295,7 +295,7 @@ type
vars: THashedStringList; vars: THashedStringList;
created, ttl, expires: Tdatetime; created, ttl, expires: Tdatetime;
public public
id: string; id, user: string;
constructor create(const sid:string=''); constructor create(const sid:string='');
destructor Destroy; override; destructor Destroy; override;
procedure setVar(const k,v:string); procedure setVar(const k,v:string);
@ -325,7 +325,7 @@ type
agent: string; agent: string;
conn: ThttpConn; conn: ThttpConn;
account: Paccount; account: Paccount;
usr, pwd: string; user, pwd: string;
acceptedCredentials: boolean; acceptedCredentials: boolean;
limiter: TspeedLimiter; limiter: TspeedLimiter;
tpl: Ttpl; tpl: Ttpl;
@ -1326,7 +1326,7 @@ if (action = FA_UPLOAD) and not f.isRealFolder() then exit;
result:=TRUE; result:=TRUE;
if stringExists(USER_ANYONE, a, TRUE) then exit; if stringExists(USER_ANYONE, a, TRUE) then exit;
result:=(cd.usr = '') and stringExists(USER_ANONYMOUS, a, TRUE) result:=(cd.user = '') and stringExists(USER_ANONYMOUS, a, TRUE)
or assigned(cd.account) and stringExists(USER_ANY_ACCOUNT, a, TRUE) or assigned(cd.account) and stringExists(USER_ANY_ACCOUNT, a, TRUE)
or (NIL <> findEnabledLinkedAccount(cd.account, a, TRUE)); or (NIL <> findEnabledLinkedAccount(cd.account, a, TRUE));
end; // accountAllowed end; // accountAllowed
@ -1562,7 +1562,7 @@ var
function allowedTo(f:Tfile):boolean; function allowedTo(f:Tfile):boolean;
begin begin
if cd = NIL then result:=FALSE if cd = NIL then result:=FALSE
else result:=(not (FA_VIS_ONLY_ANON in f.flags) or (cd.usr = '')) else result:=(not (FA_VIS_ONLY_ANON in f.flags) or (cd.user = ''))
and (seeProtected or f.accessFor(cd)) and (seeProtected or f.accessFor(cd))
and not (forArchive and f.isDLforbidden()) and not (forArchive and f.isDLforbidden())
end; // allowedTo end; // allowedTo
@ -1912,7 +1912,7 @@ while i < srv.conns.count do
if isDownloading(d) if isDownloading(d)
and ((f = NIL) or (assigned(d.lastFile) and d.lastFile.same(f))) and ((f = NIL) or (assigned(d.lastFile) and d.lastFile.same(f)))
and ((ip = '') or addressMatch(ip, d.address)) and ((ip = '') or addressMatch(ip, d.address))
and ((user = '') or sameText(user, d.usr)) and ((user = '') or sameText(user, d.user))
then then
inc(result); inc(result);
inc(i); inc(i);
@ -1931,7 +1931,7 @@ while i < srv.conns.count do
begin begin
d:=conn2data(i); d:=conn2data(i);
if not onlyDownloading or isDownloading(d) then if not onlyDownloading or isDownloading(d) then
addUniqueString(if_(usersInsteadOfIps, d.usr, d.address), ips); addUniqueString(if_(usersInsteadOfIps, d.user, d.address), ips);
inc(i); inc(i);
end; end;
result:=length(ips); result:=length(ips);
@ -2362,7 +2362,8 @@ end; // disconnect
procedure TconnData.logout(); procedure TconnData.logout();
begin begin
freeAndNIL(session); freeAndNIL(session);
usr:=''; account:=NIL;
user:='';
pwd:=''; pwd:='';
conn.delCookie(SESSION_COOKIE); conn.delCookie(SESSION_COOKIE);
end; // logout end; // logout
@ -3071,7 +3072,7 @@ begin result:=hasRecursive([attribute], FALSE, outInherited) end;
function Tfile.accessFor(cd:TconnData):boolean; function Tfile.accessFor(cd:TconnData):boolean;
begin begin
if cd = NIL then result:=accessFor('', '') if cd = NIL then result:=accessFor('', '')
else result:=accessFor(cd.usr, cd.pwd) else result:=accessFor(cd.user, cd.pwd)
end; // accessFor end; // accessFor
function Tfile.accessFor(username, password:string):boolean; function Tfile.accessFor(username, password:string):boolean;
@ -3539,11 +3540,11 @@ var
s:=url; s:=url;
end end
else else
if pwdInPagesChk.Checked and (cd.usr > '') then if pwdInPagesChk.Checked and (cd.user > '') then
begin begin
if encodePwdUrlChk.checked then s:=totallyEncoded(cd.pwd) if encodePwdUrlChk.checked then s:=totallyEncoded(cd.pwd)
else s:=encodeURL(cd.pwd); else s:=encodeURL(cd.pwd);
s:=f.fullURL( encodeURL(cd.usr)+':'+s, getSafeHost(cd) )+fingerprint; s:=f.fullURL( encodeURL(cd.user)+':'+s, getSafeHost(cd) )+fingerprint;
url:=s url:=s
end end
else else
@ -3823,7 +3824,7 @@ var
fn:=macroQuote(fn); fn:=macroQuote(fn);
// apply fields // apply fields
files:=files+xtpl(t, [ files:=files+xtpl(t, [
'%item-user%', macroQuote(d.usr), '%item-user%', macroQuote(d.user),
'%perc%',intToStr( trunc(perc*100) ), '%perc%',intToStr( trunc(perc*100) ),
'%filename%', fn, '%filename%', fn,
'%filename-js%', jsEncode(fn, '''"'), '%filename-js%', jsEncode(fn, '''"'),
@ -3908,6 +3909,7 @@ try
addUploadSymbols(); addUploadSymbols();
addProgressSymbols(); addProgressSymbols();
addUploadResultsSymbols(); addUploadResultsSymbols();
//addArray(md.table, ['%folder%', data.f.]);
if data = NIL then s:='' if data = NIL then s:=''
else s:=first(data.banReason, data.disconnectReason); else s:=first(data.banReason, data.disconnectReason);
addArray(md.table, ['%reason%', s]); addArray(md.table, ['%reason%', s]);
@ -4122,7 +4124,7 @@ var
begin begin
decodeDateFully(now(), y,m,d,w); decodeDateFully(now(), y,m,d,w);
if cd = NIL then u:='' if cd = NIL then u:=''
else u:=nonEmptyConcat('(', cd.usr, ')'); else u:=nonEmptyConcat('(', cd.user, ')');
result:=xtpl(logFile.filename, [ result:=xtpl(logFile.filename, [
'%d%', int0(d,2), '%d%', int0(d,2),
'%m%', int0(m,2), '%m%', int0(m,2),
@ -4168,18 +4170,15 @@ else
addr:=''; addr:='';
if assigned(cd) and assigned(cd.conn) then if assigned(cd) and assigned(cd.conn) then
begin addr:=nonEmptyConcat('', cd.user, '@')
addr:=cd.address+':'+cd.conn.port +cd.address+':'+cd.conn.port
+nonEmptyConcat(' {', localDNSget(cd.address), '}'); +nonEmptyConcat(' {', localDNSget(cd.address), '}');
if freeLoginChk.checked or cd.acceptedCredentials then
addr:=nonEmptyConcat('', cd.usr, '@')+addr;
end;
if (logFile.filename > '') and (logFile.apacheFormat = '') then if (logFile.filename > '') and (logFile.apacheFormat = '') then
begin begin
s:=ts; s:=ts;
if (cd = NIL) or (cd.conn = nil) then s:=s+TAB+''+TAB+''+TAB+''+TAB+'' if (cd = NIL) or (cd.conn = nil) then s:=s+TAB+''+TAB+''+TAB+''+TAB+''
else s:=s+TAB+cd.usr+TAB+cd.address+TAB+cd.conn.port+TAB+localDNSget(cd.address); else s:=s+TAB+cd.user+TAB+cd.address+TAB+cd.conn.port+TAB+localDNSget(cd.address);
s:=s+TAB+first; s:=s+TAB+first;
if tabOnLogFileChk.checked then s:=s+stripChars(reReplace(lines, '^', TAB),[#13,#10]) if tabOnLogFileChk.checked then s:=s+stripChars(reReplace(lines, '^', TAB),[#13,#10])
@ -4365,7 +4364,7 @@ try
case cmd of case cmd of
'a', 'h': res:=cd.address; 'a', 'h': res:=cd.address;
'l': res:='-'; 'l': res:='-';
'u': res:=first(cd.usr, '-'); 'u': res:=first(cd.user, '-');
't': res:='[' 't': res:='['
+replaceStr(formatDatetime(APACHE_TIMESTAMP_FORMAT, now()), +replaceStr(formatDatetime(APACHE_TIMESTAMP_FORMAT, now()),
'!!!',MONTH2STR[monthOf(now())]) '!!!',MONTH2STR[monthOf(now())])
@ -4887,6 +4886,8 @@ var
begin begin
if data = NIL then if data = NIL then
exit; exit;
data.user:='';
data.pwd:='';
if data.session = NIL then if data.session = NIL then
begin begin
sid:=conn.getCookie(SESSION_COOKIE); sid:=conn.getCookie(SESSION_COOKIE);
@ -4901,18 +4902,18 @@ var
end; end;
end; end;
data.session.keepAlive(); data.session.keepAlive();
if data.usr = '' then if conn.request.user > '' then // priority
begin begin
data.usr:=data.session.getVar('user'); data.user:=conn.request.user;
data.pwd:=data.session.getVar('password');
end;
if (data.usr = '') and (conn.request.user > '') then
begin
data.usr:=conn.request.user;
data.pwd:=conn.request.pwd; data.pwd:=conn.request.pwd;
data.account:=NIL;
exit;
end; end;
if (data.usr = '') <> (data.account = NIL) then data.account:=getAccount(data.session.user);
data.account:=getAccount(data.usr); if data.account = NIL then
exit;
data.user:=data.account.user;
data.pwd:=data.account.pwd;
end; // sessionSetup end; // sessionSetup
procedure serveTar(); procedure serveTar();
@ -5059,6 +5060,7 @@ var
var var
dlForbiddenForWholeFolder, specialGrant: boolean; dlForbiddenForWholeFolder, specialGrant: boolean;
urlCmd: string; urlCmd: string;
acc: Paccount;
function accessGranted(forceFile:Tfile=NIL):boolean; function accessGranted(forceFile:Tfile=NIL):boolean;
resourcestring resourcestring
@ -5087,11 +5089,17 @@ var
freeIfTemp(Ftemp); freeIfTemp(Ftemp);
end; end;
if result then exit; if result then exit;
if f.isFolder() then
begin
conn.reply.mode:=HRM_REPLY;
getPage('login', data, f);
exit;
end;
conn.reply.realm:=f.getShownRealm(); conn.reply.realm:=f.getShownRealm();
runEventScript('unauthorized'); runEventScript('unauthorized');
getPage('unauthorized', data); getPage('unauthorized', data);
// log anyone trying to guess the password // log anyone trying to guess the password
if (forceFile = NIL) and stringExists(data.usr, getAccountList(TRUE, FALSE)) if (forceFile = NIL) and stringExists(data.user, getAccountList(TRUE, FALSE))
and logOtherEventsChk.checked then and logOtherEventsChk.checked then
add2log(FAILED, data); add2log(FAILED, data);
end; // accessGranted end; // accessGranted
@ -5190,13 +5198,14 @@ var
function goodPassword(s:string; func:ThashFunc):boolean; function goodPassword(s:string; func:ThashFunc):boolean;
begin begin
s:=data.postVars.values[s]; s:=data.postVars.values['password'+s];
result:=(s > '') and (s = func(func(data.account.pwd)+data.session.id)) // Instead of hash(pwd+session) I replaced pwd with hash(pwd) so that in the future this may work even if we stored hashed password on the server
result:=(s > '') and (s = func(func(acc.pwd)+data.session.id))
end; end;
var var
b: boolean; b: boolean;
s: string; s, mode: string;
i: integer; i: integer;
section: PtplSection; section: PtplSection;
begin begin
@ -5270,6 +5279,7 @@ var
url:=conn.request.url; url:=conn.request.url;
extractParams(); extractParams();
url:=decodeURL(ansistring(url)); url:=decodeURL(ansistring(url));
mode:= data.urlvars.values['mode'];
data.lastFN:=extractFileName( replaceStr(url,'/','\') ); data.lastFN:=extractFileName( replaceStr(url,'/','\') );
data.agent:=getAgentID(conn); data.agent:=getAgentID(conn);
@ -5281,50 +5291,31 @@ var
end; end;
sessionSetup(); sessionSetup();
if data.postVars.indexOfName('__USER') >= 0 then if mode = 'logout' then
begin begin
s:=data.postVars.values['__USER']; data.logout();
data.account:=getAccount(s); replyWithString('ok');
if data.account = NIL then exit;
if s = '' then // logout end;
begin if mode = 'login' then
s:='ok'; begin
data.logout(); acc:=getAccount(data.postVars.values['user']);
end if acc = NIL then
else s:='username not found'
s:='username not found'
else else
begin if goodPassword('SHA256', strSHA256)
data.usr:=s; or goodPassword('MD5', strMD5)
{ I opted to use double hashing for this authentication method so that in the or (data.postVars.values['password'] = acc.pwd) then
future this may work even if we stored hashed password on the server,
thus being unable to calculate hash(pwd+sessionID).
By relying on hash(pwd) instead of pwd we avoid such problem. }
if goodPassword('__PASSWORD_SHA256', strSHA256)
or goodPassword('__PASSWORD_MD5', strMD5)
or (data.postVars.values['__PASSWORD'] = data.account.pwd) then
begin begin
data.session.user:=acc.user;
s:='ok'; s:='ok';
data.pwd:=data.account.pwd;
data.session.setVar('user', data.usr);
data.session.setVar('password', data.pwd);
end end
else else
begin
s:='bad password'; //TODO shouldn't this change http code? s:='bad password'; //TODO shouldn't this change http code?
data.account:=NIL; replyWithString(s);
data.usr:=''; exit;
end;
end;
if data.postVars.values['__AJAX'] = '1' then
begin
replyWithString(s);
exit;
end;
end; end;
// this is better to be refresh, because a user may be deleted meantime
data.account:=getAccount(data.usr);
conn.ignoreSpeedLimit:=noLimitsFor(data.account); conn.ignoreSpeedLimit:=noLimitsFor(data.account);
// all URIs must begin with / // all URIs must begin with /
@ -5349,7 +5340,7 @@ var
getPage('not found', data); getPage('not found', data);
exit; exit;
end; end;
if data.urlvars.values['mode'] = 'jquery' then if mode = 'jquery' then
begin begin
if notModified(conn,'jquery'+FloatToStr(uptime), '') then if notModified(conn,'jquery'+FloatToStr(uptime), '') then
exit; exit;
@ -5360,9 +5351,9 @@ var
// forbid using invalid credentials // forbid using invalid credentials
if not freeLoginChk.checked and not specialGrant then if not freeLoginChk.checked and not specialGrant then
if (data.usr>'') if (data.user>'')
and ((data.account=NIL) or (data.account.pwd <> data.pwd)) and ((data.account=NIL) or (data.account.pwd <> data.pwd))
and not usersInVFS.match(data.usr, data.pwd) then and not usersInVFS.match(data.user, data.pwd) then
begin begin
data.acceptedCredentials:=FALSE; data.acceptedCredentials:=FALSE;
runEventScript('unauthorized'); runEventScript('unauthorized');
@ -5401,7 +5392,7 @@ var
conn.reply.url:=f.url(); // we use f.url() instead of just appending a "/" to url because of problems with non-ansi chars http://www.rejetto.com/forum/?topic=7837 conn.reply.url:=f.url(); // we use f.url() instead of just appending a "/" to url because of problems with non-ansi chars http://www.rejetto.com/forum/?topic=7837
exit; exit;
end; end;
if f.isFolder() and (urlCmd = '') and (data.urlvars.indexOfName('mode')<0) then if f.isFolder() and (urlCmd = '') and (mode='') then
switchToDefaultFile(); switchToDefaultFile();
if enableNoDefaultChk.checked and (urlCmd = '~nodefault') then if enableNoDefaultChk.checked and (urlCmd = '~nodefault') then
urlCmd:=''; urlCmd:='';
@ -5465,7 +5456,7 @@ var
end; end;
// provide access to any [section] in the tpl, included [progress] // provide access to any [section] in the tpl, included [progress]
if data.urlvars.values['mode'] = 'section' then if mode = 'section' then
s:=first(data.urlvars.values['id'], 'no-id') // no way, you must specify the id s:=first(data.urlvars.values['id'], 'no-id') // no way, you must specify the id
else if (f = rootFile) and (urlCmd > '') then else if (f = rootFile) and (urlCmd > '') then
s:=substr(urlCmd,2) s:=substr(urlCmd,2)
@ -5500,7 +5491,7 @@ var
end; end;
if (urlCmd = '~folder.tar') if (urlCmd = '~folder.tar')
or (data.urlvars.values['mode'] = 'archive') then or (mode = 'archive') then
begin begin
serveTar(); serveTar();
exit; exit;
@ -8963,7 +8954,7 @@ if quitting then exit;
if item = NIL then exit; if item = NIL then exit;
data:=conn2data(item); data:=conn2data(item);
if data = NIL then exit; if data = NIL then exit;
item.caption:=nonEmptyConcat('', data.usr, '@')+data.address+':'+data.conn.port; item.caption:=nonEmptyConcat('', data.user, '@')+data.address+':'+data.conn.port;
while item.subitems.count < 5 do while item.subitems.count < 5 do
item.subitems.add(''); item.subitems.add('');
@ -9477,7 +9468,7 @@ while not tlv.isOver() do
FK_USERPWD: FK_USERPWD:
begin begin
data:=base64decode(data); data:=base64decode(data);
f.user:=chop(pos(':',data),1,data); f.user:=chop(':',data);
f.pwd:=data; f.pwd:=data;
usersInVFS.track(f.user, f.pwd); usersInVFS.track(f.user, f.pwd);
end; end;

View File

@ -119,6 +119,10 @@ object optionsFrm: ToptionsFrm
object accountsPage: TTabSheet object accountsPage: TTabSheet
Caption = 'Accounts' Caption = 'Accounts'
ImageIndex = 29 ImageIndex = 29
ExplicitLeft = 0
ExplicitTop = 0
ExplicitWidth = 0
ExplicitHeight = 0
DesignSize = ( DesignSize = (
797 797
385) 385)

View File

@ -1628,7 +1628,7 @@ var
if assigned(md.cd) then if assigned(md.cd) then
begin begin
usr:=md.cd.usr; usr:=md.cd.user;
if name = '%host%' then if name = '%host%' then
result:=getSafeHost(md.cd) result:=getSafeHost(md.cd)
else if name = '%ip%' then else if name = '%ip%' then
@ -2467,7 +2467,7 @@ try
if mainfrm.macrosLogChk.checked then if mainfrm.macrosLogChk.checked then
begin begin
if not fileExists(MACROS_LOG_FILE) then if not fileExists(MACROS_LOG_FILE) then
saveFile(MACROS_LOG_FILE, HEADER); saveTextFile(MACROS_LOG_FILE, HEADER);
macrosLog(fullMacro, result, md.logTS); macrosLog(fullMacro, result, md.logTS);
md.logTS:=FALSE; md.logTS:=FALSE;
end; end;

View File

@ -2993,6 +2993,8 @@ var
i: integer; i: integer;
begin begin
result:=NIL; result:=NIL;
if user = '' then
exit;
for i:=0 to length(accounts)-1 do for i:=0 to length(accounts)-1 do
if sameText(user, accounts[i].user) then if sameText(user, accounts[i].user) then
begin begin