Skip to content

Commit f2e08ef

Browse files
committed
Merge branch 'master' into ref/relay-config-endpoint
* master: (115 commits) feat: Update to JS SDK 5.6.0-beta.1 + 0.10.2 sentry-python (#14116) fix(apm): Whitelist dev.getsentry.net for local development (#14117) test(datasets): Make Sentry use generic test functions in Snuba (#14111) ref(suspect-commits): Add text changes to empty state (#14121) build: Switch to psycopg2-binary feat(api): Add option to fetch Organization details without Pr… (#13925) ref: Remove EventDetails endpoint (#14107) test(ui): Mock the onboarding learn more video (#14108) tests(acceptance): Add tests for resolving issues in Issues Li… (#14069) feat(ui): Add basic templates for Incident Rules in settings (#14112) feat(eventsv2) Add basic transaction list (#14103) ref(environments) Optimize environment queries (#14102) fix(events-v2) Add additional user attributes to the user column (#14101) fix: Don't start pageload transaction (#14115) feat: APM Sentry Frontend (#14027) ref(onboarding): Fix install promprt URL (#14106) fix(app-platform): Allow GET requests for published apps (#14109) feat: Update Group.get_latest_event to use Snuba event (#14039) ref(onboarding): Rename wizardNew -> onboarding (#14104) feat(apm): Update props to address proptype warnings for new transaction attributes (SEN-800) (#14040) ...
2 parents 36c2f4d + e92e361 commit f2e08ef

File tree

656 files changed

+28504
-13096
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

656 files changed

+28504
-13096
lines changed

.yarnrc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
1-
yarn-path "./bin/yarn"
1+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+
# yarn lockfile v1
3+
4+
25
disable-self-update-check true
6+
lastUpdateCheck 1562193150687
7+
yarn-path "./bin/yarn"

README.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
<p align="center">
44
<p align="center">
5-
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" alt="Sentry" height="72"
5+
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
6+
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" alt="Sentry" height="72">
7+
</a>
68
</p>
79
<p align="center">
810
Users and logs provide clues. Sentry provides answers.

bin/yarn

Lines changed: 15056 additions & 4186 deletions
Large diffs are not rendered by default.

netlify.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
status = 200
2020
force = true
2121

22+
# Pass referer as sentry.io to avoid CSRF validation errors.
23+
headers = {Referer = "https://sentry.io/"}
24+
2225
[[redirects]]
2326
from = "/*"
2427
to = "/index.html"

package.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,18 @@
1818
"@babel/preset-react": "^7.0.0",
1919
"@babel/preset-typescript": "^7.3.3",
2020
"@babel/runtime": "^7.0.0",
21-
"@sentry/browser": "^5.4.2",
22-
"@sentry/integrations": "^5.4.2",
21+
"@sentry/browser": "5.6.0-beta.1",
22+
"@sentry/integrations": "5.6.0-beta.1",
2323
"@sentry/typescript": "^5.3.0",
2424
"@types/lodash": "^4.14.134",
25-
"@types/react-dom": "^16.8.4",
25+
"@types/moment-timezone": "^0.5.12",
26+
"@types/papaparse": "^4.5.11",
27+
"@types/react": "^16.7.0",
28+
"@types/react-bootstrap": "^0.32.19",
29+
"@types/react-document-title": "^2.0.3",
30+
"@types/react-dom": "^16.7.0",
31+
"@types/react-router": "^3.0.20",
32+
"@types/react-virtualized": "^9.20.1",
2633
"algoliasearch": "^3.32.0",
2734
"babel-core": "^7.0.0-bridge.0",
2835
"babel-loader": "^8.0.0",

requirements-base.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ percy>=1.1.2
3737
petname>=2.0,<2.1
3838
Pillow>=3.2.0,<=4.2.1
3939
progressbar2>=3.10,<3.11
40-
psycopg2>=2.6.0,<2.8.0
40+
psycopg2-binary>=2.6.0,<2.8.0
4141
PyJWT>=1.5.0,<1.6.0
4242
pytest-django>=2.9.1,<2.10.0
4343
pytest-html>=1.9.0,<1.10.0
@@ -56,7 +56,7 @@ requests-oauthlib==0.3.3
5656
requests[security]>=2.20.0,<2.21.0
5757
selenium==3.141.0
5858
semaphore>=0.4.38,<0.5.0
59-
sentry-sdk>=0.10.0
59+
sentry-sdk>=0.10.2
6060
setproctitle>=1.1.7,<1.2.0
6161
simplejson>=3.2.0,<3.9.0
6262
six>=1.10.0,<1.11.0

requirements-test.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ pytest-timeout==1.2.1
66
pytest-xdist>=1.18.0,<1.19.0
77
responses>=0.8.1,<0.9.0
88
sqlparse==0.1.19
9+
werkzeug==0.15.5

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,20 @@ class SentrySDistCommand(SDistCommand):
9797

9898
class SentryBuildCommand(BuildCommand):
9999
def run(self):
100-
BuildCommand.run(self)
101100
if not IS_LIGHT_BUILD:
102101
self.run_command('build_integration_docs')
103102
self.run_command('build_assets')
104103
self.run_command('build_js_sdk_registry')
104+
BuildCommand.run(self)
105105

106106

107107
class SentryDevelopCommand(DevelopCommand):
108108
def run(self):
109-
DevelopCommand.run(self)
110109
if not IS_LIGHT_BUILD:
111110
self.run_command('build_integration_docs')
112111
self.run_command('build_assets')
113112
self.run_command('build_js_sdk_registry')
113+
DevelopCommand.run(self)
114114

115115

116116
cmdclass = {

src/sentry/__init__.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
"""
2-
sentry
3-
~~~~~~
4-
5-
:copyright: (c) 2010-2014 by the Sentry Team, see AUTHORS for more details.
6-
:license: BSD, see LICENSE for more details.
7-
"""
81
from __future__ import absolute_import
92

103
import os

src/sentry/__main__.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
"""
2-
sentry
3-
~~~~~~
4-
5-
:copyright: (c) 2015 by the Sentry Team, see AUTHORS for more details.
6-
:license: BSD, see LICENSE for more details.
7-
"""
81
from __future__ import absolute_import
92

103
from .runner import main
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from __future__ import absolute_import
2+
3+
from sentry import analytics
4+
5+
6+
class SentryAppInstallationUpdatedEvent(analytics.Event):
7+
type = 'sentry_app_installation.updated'
8+
9+
attributes = (
10+
analytics.Attribute('sentry_app_installation_id'),
11+
analytics.Attribute('sentry_app_id'),
12+
analytics.Attribute('organization_id'),
13+
)
14+
15+
16+
analytics.register(SentryAppInstallationUpdatedEvent)

src/sentry/api/bases/sentryapps.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,16 @@ def wrapped(self, *args, **kwargs):
4545

4646
class SentryAppsPermission(SentryPermission):
4747
scope_map = {
48-
'GET': (), # Public endpoint.
48+
# GET is ideally a public endpoint but for now we are allowing for
49+
# anyone who has member permissions or above.
50+
'GET': ('event:read',
51+
'event:write',
52+
'event:admin',
53+
'project:releases',
54+
'project:read',
55+
'org:read',
56+
'member:read',
57+
'team:read',),
4958
'POST': ('org:read', 'org:integrations', 'org:write', 'org:admin'),
5059
}
5160

@@ -118,12 +127,25 @@ class SentryAppPermission(SentryPermission):
118127
}
119128

120129
published_scope_map = {
121-
'GET': (), # Public endpoint.
130+
# GET is ideally a public endpoint but for now we are allowing for
131+
# anyone who has member permissions or above.
132+
'GET': ('event:read',
133+
'event:write',
134+
'event:admin',
135+
'project:releases',
136+
'project:read',
137+
'org:read',
138+
'member:read',
139+
'team:read',),
122140
'PUT': ('org:write', 'org:admin'),
123141
'POST': ('org:write', 'org:admin'),
124142
'DELETE': ('org:admin'),
125143
}
126144

145+
@property
146+
def scope_map(self):
147+
return self.published_scope_map
148+
127149
def has_object_permission(self, request, view, sentry_app):
128150
if not hasattr(request, 'user') or not request.user:
129151
return False
@@ -138,6 +160,12 @@ def has_object_permission(self, request, view, sentry_app):
138160
if sentry_app.owner not in request.user.get_orgs():
139161
raise Http404
140162

163+
# TODO(meredith): make a better way to allow for public
164+
# endpoints. we can't use ensure_scoped_permission now
165+
# that the public endpoint isn't denoted by '()'
166+
if sentry_app.is_published and request.method == 'GET':
167+
return True
168+
141169
return ensure_scoped_permission(
142170
request,
143171
self._scopes_for_sentry_app(sentry_app).get(request.method),
@@ -227,6 +255,12 @@ class SentryAppInstallationPermission(SentryPermission):
227255
'POST': ('org:integrations', 'event:write', 'event:admin'),
228256
}
229257

258+
def has_permission(self, request, *args, **kwargs):
259+
# To let the app mark the installation as installed, we don't care about permissions
260+
if request.user.is_sentry_app and request.method == 'PUT':
261+
return True
262+
return super(SentryAppInstallationPermission, self).has_permission(request, *args, **kwargs)
263+
230264
def has_object_permission(self, request, view, installation):
231265
if not hasattr(request, 'user') or not request.user:
232266
return False
@@ -236,6 +270,10 @@ def has_object_permission(self, request, view, installation):
236270
if is_active_superuser(request):
237271
return True
238272

273+
# if user is an app, make sure it's for that same app
274+
if request.user.is_sentry_app:
275+
return request.user == installation.sentry_app.proxy_user
276+
239277
if installation.organization not in request.user.get_orgs():
240278
raise Http404
241279

src/sentry/api/endpoints/accept_project_transfer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from sentry.api.base import Endpoint, SessionAuthentication
1212
from sentry.api.decorators import sudo_required
1313
from sentry.api.serializers import serialize
14-
from sentry.api.serializers.models.organization import DetailedOrganizationSerializer
14+
from sentry.api.serializers.models.organization import DetailedOrganizationSerializerWithProjectsAndTeams
1515
from sentry.utils.signing import unsign
1616
from sentry.models import (
1717
AuditLogEntryEvent, OrganizationMember, Organization, OrganizationStatus, Team, Project
@@ -70,7 +70,7 @@ def get(self, request):
7070
'organizations': serialize(
7171
list(organizations),
7272
request.user,
73-
DetailedOrganizationSerializer(),
73+
DetailedOrganizationSerializerWithProjectsAndTeams(),
7474
access=request.access
7575
),
7676
'project': {

src/sentry/api/endpoints/auth_config.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ def get(self, request, *args, **kwargs):
3535
# we always reset the state on GET so you dont end up at an odd location
3636
auth.initiate_login(request, next_uri)
3737

38+
# Auth login verifies the test cookie is set
39+
request.session.set_test_cookie()
40+
3841
# Single org mode -- send them to the org-specific handler
3942
if settings.SENTRY_SINGLE_ORGANIZATION:
4043
org = Organization.get_default()
@@ -55,26 +58,13 @@ def respond_authenticated(self, request):
5558
next_uri = self.get_next_uri(request)
5659

5760
if not auth.is_valid_redirect(next_uri, host=request.get_host()):
58-
next_uri = self.org_redirect_url(request)
61+
active_org = self.get_active_organization(request)
62+
next_uri = auth.get_org_redirect_url(request, active_org)
5963

6064
return Response({
6165
'nextUri': next_uri,
6266
})
6367

64-
def org_redirect_url(self, request):
65-
from sentry import features
66-
67-
organization = self.get_active_organization(request)
68-
if organization:
69-
return organization.get_url()
70-
71-
if not features.has('organizations:create'):
72-
# TODO(dcramer): deal with case when the user cannot create orgs.
73-
# This will likely cause an infinite loop right now.
74-
return '/auth/login'
75-
76-
return '/organizations/new/'
77-
7868
def get_next_uri(self, request):
7969
next_uri_fallback = None
8070
if request.session.get('_next') is not None:
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from __future__ import absolute_import
2+
3+
from rest_framework.response import Response
4+
5+
from sentry.app import ratelimiter
6+
from sentry.utils import auth, metrics
7+
from sentry.utils.hashlib import md5_text
8+
from sentry.api.base import Endpoint
9+
from sentry.api.serializers.base import serialize
10+
from sentry.api.serializers.models.user import DetailedUserSerializer
11+
from sentry.web.forms.accounts import AuthenticationForm
12+
from sentry.web.frontend.base import OrganizationMixin
13+
14+
15+
class AuthLoginEndpoint(Endpoint, OrganizationMixin):
16+
# Disable authentication and permission requirements.
17+
permission_classes = []
18+
19+
def post(self, request, organization=None, *args, **kwargs):
20+
"""
21+
Process a login request via username/password. SSO login is handled
22+
elsewhere.
23+
"""
24+
login_form = AuthenticationForm(request, request.DATA)
25+
26+
# Rate limit logins
27+
is_limited = ratelimiter.is_limited(
28+
u'auth:login:username:{}'.
29+
format(md5_text(request.DATA.get('username').lower()).hexdigest()),
30+
limit=10,
31+
window=60, # 10 per minute should be enough for anyone
32+
)
33+
34+
if is_limited:
35+
errors = {
36+
'__all__': [login_form.error_messages['rate_limited']]
37+
}
38+
metrics.incr(
39+
'login.attempt',
40+
instance='rate_limited',
41+
skip_internal=True,
42+
sample_rate=1.0
43+
)
44+
45+
return self.respond_with_error(errors)
46+
47+
if not login_form.is_valid():
48+
metrics.incr(
49+
'login.attempt',
50+
instance='failure',
51+
skip_internal=True,
52+
sample_rate=1.0
53+
)
54+
return self.respond_with_error(login_form.errors)
55+
56+
user = login_form.get_user()
57+
58+
auth.login(
59+
request,
60+
user,
61+
organization_id=organization.id if organization else None,
62+
)
63+
metrics.incr(
64+
'login.attempt',
65+
instance='success',
66+
skip_internal=True,
67+
sample_rate=1.0
68+
)
69+
70+
if not user.is_active:
71+
return Response({
72+
'nextUri': '/auth/reactivate/',
73+
'user': serialize(user, user, DetailedUserSerializer()),
74+
})
75+
76+
active_org = self.get_active_organization(request)
77+
redirect_url = auth.get_org_redirect_url(request, active_org)
78+
79+
return Response({
80+
'nextUri': auth.get_login_redirect(request, redirect_url),
81+
'user': serialize(user, user, DetailedUserSerializer()),
82+
})
83+
84+
def respond_with_error(self, errors):
85+
return Response({
86+
'detail': 'Login attempt failed',
87+
'errors': errors,
88+
}, status=400)

0 commit comments

Comments
 (0)