3 final class PhabricatorExternalAccount
4 extends PhabricatorUserDAO
6 PhabricatorPolicyInterface
,
7 PhabricatorDestructibleInterface
{
10 protected $accountSecret;
11 protected $displayName;
15 protected $emailVerified = 0;
16 protected $accountURI;
17 protected $profileImagePHID;
18 protected $properties = array();
19 protected $providerConfigPHID;
21 // TODO: Remove these (see T13493). These columns are obsolete and have
22 // no readers and only trivial writers.
23 protected $accountType;
24 protected $accountDomain;
27 private $profileImageFile = self
::ATTACHABLE
;
28 private $providerConfig = self
::ATTACHABLE
;
29 private $accountIdentifiers = self
::ATTACHABLE
;
31 public function getProfileImageFile() {
32 return $this->assertAttached($this->profileImageFile
);
35 public function attachProfileImageFile(PhabricatorFile
$file) {
36 $this->profileImageFile
= $file;
40 public function generatePHID() {
41 return PhabricatorPHID
::generateNewPHID(
42 PhabricatorPeopleExternalPHIDType
::TYPECONST
);
45 protected function getConfiguration() {
47 self
::CONFIG_AUX_PHID
=> true,
48 self
::CONFIG_SERIALIZATION
=> array(
49 'properties' => self
::SERIALIZATION_JSON
,
51 self
::CONFIG_COLUMN_SCHEMA
=> array(
52 'userPHID' => 'phid?',
53 'accountType' => 'text16',
54 'accountDomain' => 'text64',
55 'accountSecret' => 'text?',
56 'accountID' => 'text64',
57 'displayName' => 'text255?',
58 'username' => 'text255?',
59 'realName' => 'text255?',
60 'email' => 'text255?',
61 'emailVerified' => 'bool',
62 'profileImagePHID' => 'phid?',
63 'accountURI' => 'text255?',
65 self
::CONFIG_KEY_SCHEMA
=> array(
67 'columns' => array('userPHID'),
69 'key_provider' => array(
70 'columns' => array('providerConfigPHID', 'userPHID'),
73 ) + parent
::getConfiguration();
76 public function save() {
77 if (!$this->getAccountSecret()) {
78 $this->setAccountSecret(Filesystem
::readRandomCharacters(32));
81 $this->openTransaction();
83 $result = parent
::save();
85 $account_phid = $this->getPHID();
86 $config_phid = $this->getProviderConfigPHID();
88 if ($this->accountIdentifiers
!== self
::ATTACHABLE
) {
89 foreach ($this->getAccountIdentifiers() as $identifier) {
91 ->setExternalAccountPHID($account_phid)
92 ->setProviderConfigPHID($config_phid)
97 $this->saveTransaction();
102 public function unlinkAccount() {
104 // When unlinking an account, we disassociate it from the user and
105 // remove all the identifying information. We retain the PHID, the
106 // object itself, and the "ExternalAccountIdentifier" objects in the
109 // TODO: This unlinks (but does not destroy) any profile image.
113 ->setDisplayName(null)
117 ->setEmailVerified(0)
118 ->setProfileImagePHID(null)
119 ->setAccountURI(null)
120 ->setProperties(array())
124 public function setProperty($key, $value) {
125 $this->properties
[$key] = $value;
129 public function getProperty($key, $default = null) {
130 return idx($this->properties
, $key, $default);
133 public function isUsableForLogin() {
134 $config = $this->getProviderConfig();
135 if (!$config->getIsEnabled()) {
139 $provider = $config->getProvider();
140 if (!$provider->shouldAllowLogin()) {
147 public function attachProviderConfig(PhabricatorAuthProviderConfig
$config) {
148 $this->providerConfig
= $config;
152 public function getProviderConfig() {
153 return $this->assertAttached($this->providerConfig
);
156 public function getAccountIdentifiers() {
157 $raw = $this->assertAttached($this->accountIdentifiers
);
158 return array_values($raw);
161 public function attachAccountIdentifiers(array $identifiers) {
162 assert_instances_of($identifiers, 'PhabricatorExternalAccountIdentifier');
163 $this->accountIdentifiers
= mpull($identifiers, null, 'getIdentifierRaw');
167 public function appendIdentifier(
168 PhabricatorExternalAccountIdentifier
$identifier) {
170 $this->assertAttached($this->accountIdentifiers
);
172 $map = $this->accountIdentifiers
;
173 $raw = $identifier->getIdentifierRaw();
175 $old = idx($map, $raw);
181 // Here, we already know about an identifier and have rediscovered it.
183 // We could copy properties from the new version of the identifier here,
184 // or merge them in some other way (for example, update a "last seen
185 // from the provider" timestamp), but no such properties currently exist.
189 $this->accountIdentifiers
[$raw] = $result;
195 /* -( PhabricatorPolicyInterface )----------------------------------------- */
198 public function getCapabilities() {
200 PhabricatorPolicyCapability
::CAN_VIEW
,
201 PhabricatorPolicyCapability
::CAN_EDIT
,
205 public function getPolicy($capability) {
206 switch ($capability) {
207 case PhabricatorPolicyCapability
::CAN_VIEW
:
208 return PhabricatorPolicies
::getMostOpenPolicy();
209 case PhabricatorPolicyCapability
::CAN_EDIT
:
210 return PhabricatorPolicies
::POLICY_NOONE
;
214 public function hasAutomaticCapability($capability, PhabricatorUser
$viewer) {
215 return ($viewer->getPHID() == $this->getUserPHID());
218 public function describeAutomaticCapability($capability) {
219 switch ($capability) {
220 case PhabricatorPolicyCapability
::CAN_VIEW
:
222 case PhabricatorPolicyCapability
::CAN_EDIT
:
224 'External accounts can only be edited by the account owner.');
229 /* -( PhabricatorDestructibleInterface )----------------------------------- */
232 public function destroyObjectPermanently(
233 PhabricatorDestructionEngine
$engine) {
235 $viewer = $engine->getViewer();
237 $identifiers = id(new PhabricatorExternalAccountIdentifierQuery())
239 ->withExternalAccountPHIDs(array($this->getPHID()))
241 foreach ($identifiers as $identifier) {
242 $engine->destroyObject($identifier);
245 // TODO: This may leave a profile image behind.