Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F109637648
PhabricatorAuthController.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Apr 22, 19:45
Size
9 KB
Mime Type
text/x-php
Expires
Thu, Apr 24, 19:45 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
25743009
Attached To
rPH Phabricator
PhabricatorAuthController.php
View Options
<?php
abstract
class
PhabricatorAuthController
extends
PhabricatorController
{
protected
function
renderErrorPage
(
$title
,
array
$messages
)
{
$view
=
new
PHUIInfoView
();
$view
->
setTitle
(
$title
);
$view
->
setErrors
(
$messages
);
return
$this
->
newPage
()
->
setTitle
(
$title
)
->
appendChild
(
$view
);
}
/**
* Returns true if this install is newly setup (i.e., there are no user
* accounts yet). In this case, we enter a special mode to permit creation
* of the first account form the web UI.
*/
protected
function
isFirstTimeSetup
()
{
// If there are any auth providers, this isn't first time setup, even if
// we don't have accounts.
if
(
PhabricatorAuthProvider
::
getAllEnabledProviders
())
{
return
false
;
}
// Otherwise, check if there are any user accounts. If not, we're in first
// time setup.
$any_users
=
id
(
new
PhabricatorPeopleQuery
())
->
setViewer
(
PhabricatorUser
::
getOmnipotentUser
())
->
setLimit
(
1
)
->
execute
();
return
!
$any_users
;
}
/**
* Log a user into a web session and return an @{class:AphrontResponse} which
* corresponds to continuing the login process.
*
* Normally, this is a redirect to the validation controller which makes sure
* the user's cookies are set. However, event listeners can intercept this
* event and do something else if they prefer.
*
* @param PhabricatorUser User to log the viewer in as.
* @return AphrontResponse Response which continues the login process.
*/
protected
function
loginUser
(
PhabricatorUser
$user
)
{
$response
=
$this
->
buildLoginValidateResponse
(
$user
);
$session_type
=
PhabricatorAuthSession
::
TYPE_WEB
;
$event_type
=
PhabricatorEventType
::
TYPE_AUTH_WILLLOGINUSER
;
$event_data
=
array
(
'user'
=>
$user
,
'type'
=>
$session_type
,
'response'
=>
$response
,
'shouldLogin'
=>
true
,
);
$event
=
id
(
new
PhabricatorEvent
(
$event_type
,
$event_data
))
->
setUser
(
$user
);
PhutilEventEngine
::
dispatchEvent
(
$event
);
$should_login
=
$event
->
getValue
(
'shouldLogin'
);
if
(
$should_login
)
{
$session_key
=
id
(
new
PhabricatorAuthSessionEngine
())
->
establishSession
(
$session_type
,
$user
->
getPHID
(),
$partial
=
true
);
// NOTE: We allow disabled users to login and roadblock them later, so
// there's no check for users being disabled here.
$request
=
$this
->
getRequest
();
$request
->
setCookie
(
PhabricatorCookies
::
COOKIE_USERNAME
,
$user
->
getUsername
());
$request
->
setCookie
(
PhabricatorCookies
::
COOKIE_SESSION
,
$session_key
);
$this
->
clearRegistrationCookies
();
}
return
$event
->
getValue
(
'response'
);
}
protected
function
clearRegistrationCookies
()
{
$request
=
$this
->
getRequest
();
// Clear the registration key.
$request
->
clearCookie
(
PhabricatorCookies
::
COOKIE_REGISTRATION
);
// Clear the client ID / OAuth state key.
$request
->
clearCookie
(
PhabricatorCookies
::
COOKIE_CLIENTID
);
// Clear the invite cookie.
$request
->
clearCookie
(
PhabricatorCookies
::
COOKIE_INVITE
);
}
private
function
buildLoginValidateResponse
(
PhabricatorUser
$user
)
{
$validate_uri
=
new
PhutilURI
(
$this
->
getApplicationURI
(
'validate/'
));
$validate_uri
->
setQueryParam
(
'expect'
,
$user
->
getUsername
());
return
id
(
new
AphrontRedirectResponse
())->
setURI
((
string
)
$validate_uri
);
}
protected
function
renderError
(
$message
)
{
return
$this
->
renderErrorPage
(
pht
(
'Authentication Error'
),
array
(
$message
,
));
}
protected
function
loadAccountForRegistrationOrLinking
(
$account_key
)
{
$request
=
$this
->
getRequest
();
$viewer
=
$request
->
getUser
();
$account
=
null
;
$provider
=
null
;
$response
=
null
;
if
(!
$account_key
)
{
$response
=
$this
->
renderError
(
pht
(
'Request did not include account key.'
));
return
array
(
$account
,
$provider
,
$response
);
}
// NOTE: We're using the omnipotent user because the actual user may not
// be logged in yet, and because we want to tailor an error message to
// distinguish between "not usable" and "does not exist". We do explicit
// checks later on to make sure this account is valid for the intended
// operation. This requires edit permission for completeness and consistency
// but it won't actually be meaningfully checked because we're using the
// ominpotent user.
$account
=
id
(
new
PhabricatorExternalAccountQuery
())
->
setViewer
(
PhabricatorUser
::
getOmnipotentUser
())
->
withAccountSecrets
(
array
(
$account_key
))
->
needImages
(
true
)
->
requireCapabilities
(
array
(
PhabricatorPolicyCapability
::
CAN_VIEW
,
PhabricatorPolicyCapability
::
CAN_EDIT
,
))
->
executeOne
();
if
(!
$account
)
{
$response
=
$this
->
renderError
(
pht
(
'No valid linkable account.'
));
return
array
(
$account
,
$provider
,
$response
);
}
if
(
$account
->
getUserPHID
())
{
if
(
$account
->
getUserPHID
()
!=
$viewer
->
getPHID
())
{
$response
=
$this
->
renderError
(
pht
(
'The account you are attempting to register or link is already '
.
'linked to another user.'
));
}
else
{
$response
=
$this
->
renderError
(
pht
(
'The account you are attempting to link is already linked '
.
'to your account.'
));
}
return
array
(
$account
,
$provider
,
$response
);
}
$registration_key
=
$request
->
getCookie
(
PhabricatorCookies
::
COOKIE_REGISTRATION
);
// NOTE: This registration key check is not strictly necessary, because
// we're only creating new accounts, not linking existing accounts. It
// might be more hassle than it is worth, especially for email.
//
// The attack this prevents is getting to the registration screen, then
// copy/pasting the URL and getting someone else to click it and complete
// the process. They end up with an account bound to credentials you
// control. This doesn't really let you do anything meaningful, though,
// since you could have simply completed the process yourself.
if
(!
$registration_key
)
{
$response
=
$this
->
renderError
(
pht
(
'Your browser did not submit a registration key with the request. '
.
'You must use the same browser to begin and complete registration. '
.
'Check that cookies are enabled and try again.'
));
return
array
(
$account
,
$provider
,
$response
);
}
// We store the digest of the key rather than the key itself to prevent a
// theoretical attacker with read-only access to the database from
// hijacking registration sessions.
$actual
=
$account
->
getProperty
(
'registrationKey'
);
$expect
=
PhabricatorHash
::
weakDigest
(
$registration_key
);
if
(!
phutil_hashes_are_identical
(
$actual
,
$expect
))
{
$response
=
$this
->
renderError
(
pht
(
'Your browser submitted a different registration key than the one '
.
'associated with this account. You may need to clear your cookies.'
));
return
array
(
$account
,
$provider
,
$response
);
}
$other_account
=
id
(
new
PhabricatorExternalAccount
())->
loadAllWhere
(
'accountType = %s AND accountDomain = %s AND accountID = %s
AND id != %d'
,
$account
->
getAccountType
(),
$account
->
getAccountDomain
(),
$account
->
getAccountID
(),
$account
->
getID
());
if
(
$other_account
)
{
$response
=
$this
->
renderError
(
pht
(
'The account you are attempting to register with already belongs '
.
'to another user.'
));
return
array
(
$account
,
$provider
,
$response
);
}
$provider
=
PhabricatorAuthProvider
::
getEnabledProviderByKey
(
$account
->
getProviderKey
());
if
(!
$provider
)
{
$response
=
$this
->
renderError
(
pht
(
'The account you are attempting to register with uses a nonexistent '
.
'or disabled authentication provider (with key "%s"). An '
.
'administrator may have recently disabled this provider.'
,
$account
->
getProviderKey
()));
return
array
(
$account
,
$provider
,
$response
);
}
return
array
(
$account
,
$provider
,
null
);
}
protected
function
loadInvite
()
{
$invite_cookie
=
PhabricatorCookies
::
COOKIE_INVITE
;
$invite_code
=
$this
->
getRequest
()->
getCookie
(
$invite_cookie
);
if
(!
$invite_code
)
{
return
null
;
}
$engine
=
id
(
new
PhabricatorAuthInviteEngine
())
->
setViewer
(
$this
->
getViewer
())
->
setUserHasConfirmedVerify
(
true
);
try
{
return
$engine
->
processInviteCode
(
$invite_code
);
}
catch
(
Exception
$ex
)
{
// If this fails for any reason, just drop the invite. In normal
// circumstances, we gave them a detailed explanation of any error
// before they jumped into this workflow.
return
null
;
}
}
protected
function
renderInviteHeader
(
PhabricatorAuthInvite
$invite
)
{
$viewer
=
$this
->
getViewer
();
// Since the user hasn't registered yet, they may not be able to see other
// user accounts. Load the inviting user with the omnipotent viewer.
$omnipotent_viewer
=
PhabricatorUser
::
getOmnipotentUser
();
$invite_author
=
id
(
new
PhabricatorPeopleQuery
())
->
setViewer
(
$omnipotent_viewer
)
->
withPHIDs
(
array
(
$invite
->
getAuthorPHID
()))
->
needProfileImage
(
true
)
->
executeOne
();
// If we can't load the author for some reason, just drop this message.
// We lose the value of contextualizing things without author details.
if
(!
$invite_author
)
{
return
null
;
}
$invite_item
=
id
(
new
PHUIObjectItemView
())
->
setHeader
(
pht
(
'Welcome to Phabricator!'
))
->
setImageURI
(
$invite_author
->
getProfileImageURI
())
->
addAttribute
(
pht
(
'%s has invited you to join Phabricator.'
,
$invite_author
->
getFullName
()));
$invite_list
=
id
(
new
PHUIObjectItemListView
())
->
addItem
(
$invite_item
)
->
setFlush
(
true
);
return
id
(
new
PHUIBoxView
())
->
addMargin
(
PHUI
::
MARGIN_LARGE
)
->
appendChild
(
$invite_list
);
}
}
Event Timeline
Log In to Comment