Allow users to opt-in for automatic subscription to shares
Description
Evaluate different approaches to implement the feature of automatically subscribing external users to a share.
On share form any user can invite watchers:
We will send email invite to users.
We create account if email is not in DB.
We need to track all invites.
Email with link that render share and automatically subscribes to ticket.
We track if user accepted invitation.
We only send 1 invite to email.
If there is no subscribe_auto record - we send invitation email.
We create subscribe_auto record with NO_DECISION.
If there is subscribe_auto record with NO_DECISION / REJECT - we do not send invitation email.
we do not send more invitations via email from atlassian_host.
We do not create more subscribe_invitation for that user.
This will ensure our service wont be used as spam tool.
If Jira instance has more than 50 un-accepted invitations in last 30 days, we do not send more invitations via email.
User can see on dashboard new tab with invitations. Accepted, rejected, without-decision.
subscribe_invitation |
||
---|---|---|
id |
bigint |
|
host_id |
int |
|
share_id |
bigint |
|
invited_user_id |
bigint |
user to whom we send invite |
sender_user_id |
bigint |
user who did send invite |
atlassian_sender_user_id |
||
status |
enum {ACCEPTED, NO_DECISION} |
by default NO_DECISION |
create_date |
date-time |
|
action_date |
date-time |
date when ACCEPT |
subscribe_auto |
||
---|---|---|
id |
bigint |
|
host_id |
int |
|
invited_user_id |
bigint |
user to whom we send invite |
status |
enum {ACCEPTED, NO_DECISION, REJECTED} |
by default NO_DECISION |
create_date |
date-time |
If user rejects all watch invitation - he can get another invitations notifications.
We base our spam protection based on NO_DECISION.
What happens during the invite action:
-
New record inserted to subscribe_auto (if no record)
by default NO_DECISION -
New record inserted to subscribe_invitation,
status NO_DECISION - if subscribe_auto is NO_DECISION or REJECTED
status ACCEPTED - if subscribe_auto is ACCEPTED
Example invite email
Subject: You are invited to subscribe to {SHARE_NAME}
Hello,
You can subscribe to {SHARE_NAME} by clicking:https://jira.external-share.com/subscribe/{id-hash}
Thanks,
Externals Share for Jira Team
Link:https://jira.external-share.com/subscribe/{id}/{hash}
{id} - invitation id
{hash} - hash(invitation id + share.secret_key)
Action - the link is clicked, then:
we change the status in subscribe_auto to ACCEPTED
status in subscribe_invitation to ACCEPTED.
If the user has an unconfirmed account (i.e. registration status is initial or pending),
then we redirect to the registration completion page https://jira.external-share.com/activate.html?code={code}
.
If user has confirmed account, redirect to share /issue/{share-uuid}
Dashboard - 2 new tabs/tables.
1. Subscribe invitations
Share name |
Invited by |
Invited from |
---|---|---|
Example Roadmap |
John |
example.atlassian.net |
Example Roadmap 2 |
Emilli |
example2.atlassian.net |
In case Invited by is atlassian_sender_user_id then:
we retrieve the name asynchronously.
In the table we insert <div atlassian-acconut-id='12345'>12345</div>.
In html header:
<meta name="atlassian-acconut-id-fetch" content="{signed-url-to-fetch-all-account-ids}">
signed-url-to-fetch-all-account-ids
= /atlassian-user/{jwt}
jwt
→ payload - list of atlassian-acconut-id - signed. Example - status change on share ticket.
2. Subscribe permissions -
A list of hosts from which we allow an external user to be added to the subscription automatically (without confirmation). With the ability to undo this.
Jira name |
Status |
Action |
---|---|---|
example.atlassian.net |
Allowed |
<Block> |
example2.atlassian.net |
Blocked |
<Allow> |
If user is logged to account A and gets invite for account B, we should show error page with message like: “Subscription invitation is for another account”. Subscription invite should not be accepted.
Additional notes:
-
Pagination should be done on Subscribe invitations and Subscribe permissions tabs.
-
In Subscribe invitations and Subscribe permissions tables status should look like a status in subscriptions tab.
-
Invitation date should be added to Subscribe invitations table.
All information about task can be found in description and comments. In case of any questions feel free to contact me.
Hello @Mykhailo Iereshchenko
This is the best moment to add more information that can be helpful for tester.
What areas are affected?
What are potential edge cases?
Was it checked for XSS problems?
Does change affect security, is new data exposed?
Please attach - Before / After screenshot if possible.
@Mykhailo Iereshchenko There is 1 non-blocking issues
Hello @Mykhailo Iereshchenko,
Change was reviewed and approved.
Task is ready to be deployed to QA.
Once it is deployed to QA please move ticket to "To Test"
Thank you!
@Mykhailo Iereshchenko Yea. 1 hour sounds good
@Krzysztof Bogdan There is a limitation of 50 unaccepted invitations send from one instance in past 30 days. You asked to reduce number of unaccepted invitations from 50 to 5 on qa and local environments. Should I also reduce number of days? If yes, to what value? 1h would be ok?
@Mykhailo Iereshchenko Why does it matter. It is about relation with host_id. not user id?
@Krzysztof Bogdan In DB we have two columns which identify user who sent the invitation:
atlassian_sender_user_id
andapp_sender_user_id
. In LOCAL/QA tab we show for example “Number of sent and accepted by invited users invitations”. So this should be a number of invitations which where send byatlassian_sender_user_id
(i.e. from preview)? Because we cannot detect app_sender_user_id being in jiraIn additional feature requested by @Krzysztof Bogdan there should be:
New tab which will be available only in local and qa environments
Number of sent and accepted by invited users invitations
Number of sent and unaccepted by invited users invitations, which should be set to 5 for local and qa environment and 50 for production
Status blocked if number if unaccepted invites reaches the limit
Reset button which deletes all unaccepted invites
@Mykhailo Iereshchenko I know that would be simpler.
But… we can not do that.
If list have 100 rows.
That would mean 100 HTTP CALLS to Atlassian.
That would mean loading list will be very, very slow.
If we can we should load related data in async manner without blocking page load.
Also we should gather all user ids and fetch users in 1 http call.
@Krzysztof Bogdan
Should this part be done the exact same way you described? Because I could just make a call to “/rest/api/3/user?accountId=” and retrieve the name because that's all we need to display in the table. It seems to me that this would be simpler. Below is the function that retrieves all invitations for a given user.
atlassianSenderUserId
is used to retrieve a name of the user which sent the invite.@Krzysztof Bogdan If our app_user is currently logged into account A, gets an invitation to account B and clicks on the link in the email, should he be logged out of the current account (A) and be redirected to /login.html? If so, should the invitation be accepted as soon as user clicks the link, or only after logging in?
Or maybe, it would be better not to log in and just display some page with a message like “You've successfully accepted watcher invitation”?
Currently, when I just redirect to /login.html it automatically logs into the account I'm currently logged (A).