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

133
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.account:=getAccount(s);
if data.account = NIL then
if s = '' then // logout
begin
s:='ok';
data.logout(); data.logout();
end replyWithString('ok');
else exit;
end;
if mode = 'login' then
begin
acc:=getAccount(data.postVars.values['user']);
if acc = NIL then
s:='username not found' s:='username not found'
else else
if goodPassword('SHA256', strSHA256)
or goodPassword('MD5', strMD5)
or (data.postVars.values['password'] = acc.pwd) then
begin begin
data.usr:=s; data.session.user:=acc.user;
{ I opted to use double hashing for this authentication method so that in the
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
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;
data.usr:='';
end;
end;
if data.postVars.values['__AJAX'] = '1' then
begin
replyWithString(s); replyWithString(s);
exit; 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