Sentry新版SSO Provider讲解

从GH-1372 issue完成时开始,Sentry 7.x转向使用自家定义的SSO Provider,逐渐抛弃django-social-auth结构。因为缺乏文档,我在此事上消耗了大量的时间。现在写这篇Wiki用于记录:

基类:sentry/auth/provider.py 中的 Provider 虚基类

样例:sentry/auth/providers/ 目录下的dummy.py和oauth2.py 两个,以及sentry-sso-google 基于Sentry自带的OAuth2Provider

 

基本执行流程:

  1. 通过django机制加载app,在初始化时调用sentry.auth.manager的register方法,注册自己的名字和class
  2. 管理员在 Organization 的Auth页开启SSO时,会生成上述类的一个实例。Sentry的helper.py负责此流程,先调用get_auth_pipeline()函数,取得一个成员为多个 AuthView的超类 的list,然后对着这个list依次执行成员的 dispatch()函数
  3. 每个步骤按照pipeline的顺序执行一个AuthView.dispatch(self,request,helper)函数,其中request可以读到当前的django request内容。因为URL都为/auth/sso/ 所以需要根据querystring来判断执行到哪一步了,并收集本步的信息,调用 helper.bind_state() 保存起来,然后return help.next_step()跳到下一步执行
  4. 最后build_identify()会被执行,需要返回一个至少包含id、email、name的dict,此dict被Sentry用来构造用户信息。
Posted in 默认分类 | Tagged , | Leave a comment

sentry-公司内IM插件

这插件被大家催了很久了,到1月底终于决定动手做。

准备知识

公众号Pub服务API(内网wiki,链接已删除)

签名请求规范(内网wiki,链接已删除)

New Forms of Authentication

插件组成

  • setup.py 安装脚本。插件注册发现机制请参见
  • sentry_IM/__init__.py 确保这个目录是python package的文件。顺便查询一下VERSION供引用
  • sentry_IM/requests_APISign.py 上述New Forms of Authentication文档里提到的requests auth class,提供公司的Authencation验证header
  • sentry_IM/send_IM.py 发消息用的Class
  • sentry_IM/plugin.py 插件本身

程序讲解

插件本身继承自sentry.plugins.bases.notify.NotificationPlugin类,需要实现其notify(self, notification)方法。因基类里notify()调用了notify_users(),所以pylint会有提示。

参数notification为一个对象,可以从中取得event,然后从event取得所属的group以及group所属的project。根据project可以取得要通知的人员列表。(这里抄袭了MailPlugin的发送列表)因为这个函数得到的人员列表是Sentry里的User id,所以还需要再根据id查询到User,然后从其Email字段中取得localpart

最后,根据上述project.name、event.message、group.get_absolute_url() 组装其通知消息文本(URL在IM客户端渲染时才变成超链接,我这里只发URL本身),生成send_IM.py 的实例,对着上述人员列表发送消息文本。

IM插件所需的四个参数,在总的配置文件中列出,由插件生成send_IM实例时传递,然后由其传递给requests_APISign的实例,完成对HTTP请求的签名

配置是否开启

本来是想像MailPlugin那样直接在Account/Notification页做个列表供勾选。但阅读代码发现,这个功能并不是MailPlugin自己的功能,而是sentry做了特殊处理,先获得用户订阅的projects_list,然后循环生成ProjectEmailOptionsForm的list,随后再执行从各插件获取的ext_forms[]配置表单。ext_forms里每一个插件配置表单的初始化参数只有plugin、user和prefix,要想表达“所有可订阅的projects”这个概念会相当啰嗦。因此,最终确定为:表单里只勾选per-User 级别是否开启;而用户是否属于具体某个event/project的发送目标,则直接共用MailPlugin那边的选择,然后再用上述per-User级别开关做一遍过滤。这里两个插件之间略有粘连,主要是由于User-Project之间只有单向对应关系,逆向查询太罗嗦导致的。

image2016-2-3 16-17-40

最终发送效果

image2016-2-3 16-19-56

Github地址 https://github.com/julyclyde/sentry-IM

Posted in 默认分类 | Tagged , | Leave a comment

Sentry 新版插件基类讲解

根据 sentry/plugins/bases/__init__.py 的说明,新版插件分类四类,其中常见的:

  • NotificationPlugin 比如sentry_mail、sentry_webhook插件

NotificationPlugin的基类定义参见 sentry/plugins/bases/notify.py

Posted in 默认分类 | Tagged , | Leave a comment

Sentry整理杂记

本讨论均基于Sentry 7.7版本

插件机制

自带插件 src/sentry/plugins/ 每插件一个目录

自带插件loader:src/sentry/conf/server.py 里的INSTALLED_APPS tuple

外装插件loader:utils/runner.py 里的 install_plugins()函数,对iter_entry_points()遍历并将其加入INSTALLED_APPS tuple中

外装插件注册:插件的setup.py里执行注册entry_points的过程。例https://github.com/getsentry/sentry-groveio/blob/master/setup.py

参考setuptools:pkg_resources

列出本Python安装的Sentry插件
#!/usr/bin/env python
import pkg_resources
for ep in pkg_resources.iter_entry_points('sentry.apps'):
    print str(ep)
for ep in pkg_resources.iter_entry_points('sentry.plugins'):
    print str(ep)

 

sentry-jira插件

插件基本配置

每Project分别配置,需要输入jira的instance URL、用户名和密码。以上内容base64存在数据库sentry_projectoptions表里 where `key` like ‘jira%’

配置用户名密码之后,选择Sentry project关联到哪个JIRA Project,保存设置,并Enable Plugin即可。

 

Due Date问题

在Sentry Event页面右边点击Create JIRA Issue进入创建页面。但下面Due Date总提示Operation value must be a string 。

 点击展开故障详情

也就是说,如果不改sentry-jira插件,就无解。所以我提交了pull request https://github.com/thurloat/sentry-jira/pull/71

5日下午突然发现,目前线上Sentry 6.4.4的duedate显示为django的SelectDateWidget,而不像我自己的测试装Sentry 7.7一样直接用文本框。经过仔细对比,发现ops-sentry01上的sentry-jira插件是修改过的版本,forms.py文件class JIRAIssueForm新增了一段

site-packages/sentry_jira/forms.py片段
144
145
146
147
148
149
150
151
duedate = forms.DateField(
    label="duedate",
    #widget=adminwidgets.AdminDateWidget(),
    widget=SelectDateWidget(),
    initial=datetime.datetime.now(),
    #widget=DateWidget(usel10n=True)
    required=True
)

但这段代码没在公司的git库里保存,我就做了tree diff另外保存了。

SSO集成

厂家SSO讨论:https://github.com/getsentry/sentry/issues/1372

新版Sentry有auth backend基类https://github.com/getsentry/sentry/tree/master/src/sentry/auth/

SENTRY_FEATURES['organizations:sso']改为True可以开启Auth页面,设置sso。

目前,我参考sentry-sso-google写出来的sentry-sso-sankuai放在公司内网git库。

SENTRY_SINGLE_ORGANIZATION=True会导致/auth/login/ 跳转到 /auth/login/org_slug/ ,从而无法登录非SSO的用户(如系统自带的名为sentry的超级用户)。所以我在staging环境里关闭了这个参数。

Organizations

当SENTRY_SINGLE_ORGANIZATION=False时,utils/runner.py加载配置之后会将SENTRY_FEATURES[‘organizations:create’]强制改为False,从而禁用了右上角新创建Organization的“加号”链接。如果在此状态下删除了最后一个Organization,则其中的Team会变成游离状态,只能改掉参数重启服务重新创建org了,而且重建之后某种情况下会导致游离状态的Team丢失。切记不要删除Organization!!

根据getsentry原厂GH-1372号issue,每个Organization只能开启一个SSO AuthProvider。

目前看来,咱们的使用方法对多个Organization并没有需求。

 

新来的SSO用户默认属于所有Team的问题

经阅读代码文件web/frontend/accounts.py 发现在SENTRY_SINGLE_ORGNAZATION=True时会默认设置新注册用户的has_global_access值为True,然后如果有authprovider的话,再用authprovider.default_global_access更新该值;查看auth/helper.py 也发现会在登录和关联身份时,用 authprovider.default_global_access 给用户的has_global_access赋值。

2015年10月23日,将sentry_organizationmember表中 user_id in (35, 66, 67, 69, 71, 72, 73, 74, 77, 78, 81, 83, 84, 85, 86, 88, 89, 91, 92, 95, 96, 97, 98, 99, 100, 102, 103, 105, 107, 108, 109, 110, 112, 113, 114, 115, 117, 118, 119, 120, 122, 124, 125, 126, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 164, 166, 167, 168, 172, 173, 174, 175, 176, 177, 178, 180, 182, 183, 184, 185, 186, 187, 188, 189, 191, 192, 193, 194, 195, 196, 198, 199, 200, 203, 204, 205, 206, 207) 的111位用户的 has_global_access 值改为0,并将sentry_authprovider中id=2,provider=Sankuai的default_global_access字段改为0。

至于如何在SSO插件代码中设置,以便Sentry激活该SSO authprovider时自动将 sentry_authprovider 中 default_global_access字段设置为0,尚须进一步研究。将 orgnazation和authprovider关联的代码在web/frontend/organization_auth_settings.py 但其中并没有涉及default_global_access的内容,也许这个值并不是由authprovider影响的?

Team Admin无权批准人员加入的问题

这一代 Sentry 的权限系统设计的很烂,把 一个人属于哪个Team、一个人有权管理哪个Team混在一起了,无法表达“一个人是TeamA的member,同时是TeamB的admin”的意思。所以我一直倾向于授予较低的权限,但导致了事故。但10月29日由杨明芽发现 Team Admin 无法邀请、批准人员加入本Team,所以写了个后台任务定时批准requests。

Posted in 默认分类 | Tagged , | 1 Comment

SSO跳回sentry失败的解决方法

从某月开始,公司SSO回传信息改用POST方式,放弃了之前的通过querystring传递的做法。具体到Sentry这里,因为 /auth/sso 页面受CSRF保护,拒绝接受POST回来的不含CSRF token的数据,从而无法登录。

解决这个问题,可能有三种方法吧:

  1. 让SSO回传csrf token:前提是得先把csrf token发给SSO,且SSO方愿意配合修改。考虑到现在跳转到SSO去是直接302的,CSRF如果放在querystring上,其实和他们回传时把token放在querystring上风险相当了,所以这做法不行
  2. Sentry/auth/sso不验证CSRF:/auth/sso 对应AuthProviderLoginView,该View继承自BaseView,而BaseView的dispatch()方法是@method_decorator(csrf_protect)过的,因为还有其它很多View都这么继承,想改基类还怕有别的风险。想在配置文件里monkey patch掉AuthProviderLoginView,但是import sentry.auth.helper的时候就失败了
  3. Sentry整体不验证CSRF:试了试,csrf view middleware可以去掉,但是,csrf_protect decorator依然生效。关于此处,django文档说的不太精确。该middleware除了负责种cookie,还负责验证,但实际上验证工作并不是以middleware的身份来做的,而是以decorator的身份来做的。单单禁止middleware的加载只是去掉了种cookie的行为而已。网上也有其他人遭遇过这个问题 http://www.douban.com/group/topic/11555679/

最后,参照3里帖子的做法,做了一个django middleware插在csrf view middleware前面,如当前请求URI为/auth/sso/ 就取消CSRF保护。

Posted in 默认分类 | Tagged , | Leave a comment

记一次Sentry的性能调优过程

大概从6月底开始,我们的Sentry(错误日志收集、聚合和报警系统 http://getsentry.com )遭遇了性能问题,每分钟只能处理200个事件了,经常有20多万待处理的任务积压在events队列里,延迟超过一小时,我不得不丢弃这些任务,清空队列,以促进时效。重启一下celery worker会有瞬间的改善,但很快就又不行了,似乎worker的性能会衰减。为此,我给celery worker增加了–time-limit参数,使worker不会在执行不下去的时候无限等待,而是超时出错退出,迎接新的任务,情况略有改善。

因为Sentry的结构是前端web-消息队列-后端celery这样的结构,给调试也造成了一些困难。

我先开启了celery worker的DEBUG级别日志,从中搜索包含succeed的行,然后把其中UUID给过滤掉,只保留任务名字和耗时,整理后得到任务和耗时的对应关系,发现save_event这个任务的耗时很有意思,经常是6秒的整倍数。

然后去这个任务里添加时间打点代码,发现在它调用的EventManager.save()函数里,带事务执行的三次数据库插入(调用_save_aggregate、创建EventMapping、保存Event)的耗时,不是零点几秒,就是六秒的整倍数,而完全没有2、3秒、7、8秒这种情况。于是检查的方向就被引导到了数据库方面。折腾了DBA同事几天,删了几个索引和外键约束,也没什么改善。

想到之前为了Sentry的下一个版本v8准备过一套PostgreSQL数据库用于测试,我就拿过来,另外建了一套Sentry 7的部署,在上面运行,没想到性能极其丝滑……事情似乎越来越明了,就是数据库的问题。不过当我停止使用用于测试的128G内存真机PostgreSQL,而自己申请了一个和当前MySQL一样配置的16G机器的时候,发现低配置时pgsql的性能好像还不如MySQL……不过就在我从真实环境往测试环境export/import数据的时候,发现了Sentry在执行import时,如果测试环境共用生产系统的Redis,就会速度极慢,几乎卡死,用strace上去看一下发现慢在Redis操作上,开销在数据库上的时间极少;而如果用专用的一套Redis就速度极快。

于是我的注意力又转向了Redis。尝试FLUSHDB,发现性能立刻恢复了每分钟4000条、峰值8000条的处理能力,但redis的内存碎片率上涨了不少。看了看SLOWLOG,发现有一条几乎占满了整个SLOWLOG排行榜的命令

ZRANGE b:p 0 -1

手工试了试,这条命令得好几秒种才能返回,一次就能返回数据好几万条。搜了一下源代码,在sentry/buffer/redis.py文件发现了b:p这个key名字。原来,这是把高速更新的计数器的多次更新合并起来,减轻数据库压力的一个组件。但这个组件本身因为把sorted set用到了极限,所以性能不佳。考虑到不能再让碎片率上涨,我只好偶尔手工删除这个key,放弃一些计数器更新操作:放弃更新计数器总比放弃整个任务看起来要好些吧。

去软件原厂求助(https://github.com/getsentry/sentry/issues/3870),作者说我该上供啦,不要自己架服务啦,还是买原厂服务吧……

现在,我更改了worker的配置,把这个任务单独拉出来,用六台worker伺候着,终于基本解决了问题。

 

顺便说一下我在Redis 2.8.7上遇到的极限值:

sorted set的长度在100万左右就会性能崩溃。如果有依赖这个特性的,请大家注意有可能会引起整体系统的性能雪崩。

Posted in 默认分类 | Tagged , | 3 Comments

etcd的discovery URL是怎么回事

今天玩 CoreOS,得知他们提供了一个 etcd discovery service https://discovery.etcd.io/new?size=初始数量

当足够数量的etcd按照这个URL相互发现之后,就能成功组成集群。

问题是:

这个数量是怎么被各个etcd知道的呢?搜了一些文章,也谈到了自建 discovery service的方法,但URL似乎都是/v2/keys/random_ID/_config/size 这样的,而不像 discovery.etcd.io 这样是 /random_ID 。不过后来试了试访问 https://discovery.etcd.io/random_ID/_config/size 发现能得到那个值,就明白了,这个网址其实有rewrite,把 /random_ID 给proxy_pass到后面etcd的/v2/keys/random_ID 了,让这个URL看起来更友好,但却更迷惑了。

参考资料:https://github.com/coreos/etcd/blob/master/discovery/discovery.go#L214-L217

Posted in 默认分类 | Tagged | Leave a comment

免签过境韩国、坐船去日本手记

我总有一种不愿意走回头路的情结,虽然给自己找了许多麻烦,依然乐此不疲。
这一次,经@tigerWu 提醒,我打算从韩国坐船去日本,以期获得一个日本的“上陆许可”。虽说日语里上陆就是入境的意思,但在汉语看来,有明确的从下到上的方向指示,也就是说得从海里上来才算。

去韩国驻华大使馆网站看看,中国公民持日本旅游非团体签证可以免签证过境韩国并给予30天入境期,查验标准是下一段“机票”。但我想坐船啊!那会不会是翻译有问题呢?要知道韩国大使馆上的汉语文件基本上行文都狗屁不通。再去看看韩国出入境管理局的网站,其英文版写了“flight ticket”。
这么看来是没戏了?我不甘心,就给“国民申闻鼓”写信,等不及回答又给出入境管理局的immigration contact center打电话,第一次问仁川机场进、釜山海港出,被拒了。不过比较有意思的是这个呼叫中心并不是直接做出行政解释,而是客人报告了自己的行程计划之后,她们代为向入境口岸当局咨询。考虑到中国对外国人的72小时免签证政策限定外国人不能离开口岸城市,我就尝试咨询釜山机场进、釜山口岸出,获得了允许,遂立刻买票,但为时已晚,只买到了没有头等舱的一架飞机的商务舱。(插播八卦:东航卖大韩航空的票比大韩自己卖的便宜)与此同时,我之前向韩国信访局“国民申闻鼓”递交的信访“是否可以空进海出,没指定具体口岸咨询”也收到了回答,我还到处求爷爷告奶奶找人翻译,后来发现其实有汉语翻译版的回答,就是:不限定转乘空路或海路。我把这一页打印出来了。

2月4日,大韩KE830,办理登机牌遇到了麻烦,地服代理请示大韩航空工作人员,我出示联程船票和信访记录之后拿到登机牌;中国边检出境,按规定应该查验联程客票,但都没理我就直接把中国出境章盖在了日本签证旁边。
到釜山机场,iPad丢了,于是落了单,在后面跟机组一起,机组走专用通道,我走外国乘客通道,是一位大妈入境官。入境官翻了半天,我主动提醒我是visa free transit资格,然后出示票。该女入境指着班次号问:这是flight吗?我说是ship,她拿不准,就问韩国人通道那个男入境官,然后我就被提到那个通道受审去了。因为JR九州beetle航运公司的船票确认页只写“釜山发”没说去哪儿,男入境官不能确认这是正确的票,我又重申了我从Busan international ferry
Terminal走,要去日本的Fukuoka,但他还是将信将疑,又问我是不是crew,我说不是之后,被拉出来去条凳上坐着(传说中的边检小黑屋?),入境现场指挥官,一位大妈过来问我,入境期间准备去哪儿玩,我说JSA(也就是板门店)她说板门店很远啊你知道吗,我说我做坐韩国高铁KTX,我有KR PASS,一种外国人专用的票。然后各位入境官扔下我不理,带着护照走了!!!这时船票在他们手里,信访函在我手里。过了一会儿,指挥官和男入境官把我带到入境通道,先盖章,然后手写了截止日期,又写了个不知道是TS还是TIS的字在入境章下面,这时还扣着,又说了我几句应该详细说明啊什么什么的,然后才用摄像头拍照,又说了我几句,最后主动和我握手,这事入境官的表情是很友好的。

2月8日,我提了行李到釜山火车站,经案内所工作人员指点,到站后去坐去往ferry的shuttle bus,但是没找到,只好打了个出租车去。一打吓一跳,原来google maps标的那个terminal现在已经不用了,新的在站后的左前方(东北),出租车到南边路口掉头从我到港口。巧的是,好像韩国人找零算法和美国人一样,出租车费2800、咖啡4800,我给整几千加三百,他们很难理解我是要一个500硬币找零。值得注意的是,港口的指示牌写boarding为“登机、搭乘”,写“便名”为“flight”,甚至有个指示牌写“快速船 登机口 flyer gate”我一直以为flight是飞机航班,而船班叫voyage呢!!

日本入境,边检极其顺利,其中问了我要去哪儿,我说福冈、仙台、东京,问我是飞过去吗,我说我有JR PASS,坐火车去,就给我贴了从海里上博多港陆地的“上陆许可”一枚,其实和上次大阪关西机场的只差下边的地名不一样。海关,又跟上次一样搜我的箱子,我长得有那么像走私犯吗?而且那个女关员还钓鱼执法,问我是不是住本地,我说不是啊我是中国人,然后就问那海关卡上那个地址是什么,我说那是hotel;然后又问我去哪些地方玩;最后甚至指着签证问这是什么,我说那是签证啊!!然后就过关了。他们是在考察什么?

结论:由于韩国出入境管理局各口岸部门执行政策不一致,目前这么走并不是一个很靠谱的方案,而且边检入境问话有时会有陷阱,比如问我是不是crew、问境内怎么交通之类的,稍有不慎就有可能被认为是有问题的。韩国边检把我扔在冷板凳好几分钟,谁知道是去打电话核实船票还是打电话请示政策去了……
《中国合伙人》教导我们:真实、自信、具体、合理,是签证的基本思想基本哲学,我的行程是真实的,就不怕核查、我的行程是符合信访回信解释的,也应该能进去。
不过我觉得,我得以自己的真实经历,去督促船运公司把票写清楚,不要只写起点站。

2月8日,写于博多

Posted in 默认分类 | Tagged , , , , | Leave a comment

Sentry整理杂记

本讨论均基于Sentry 7.7版本

插件机制

自带插件

src/sentry/plugins/ 每插件一个目录
自带插件loader:src/sentry/conf/server.py 里的 INSTALLED_APPS tuple
外装插件loader:utils/runner.py 里的 install_plugins()函数,对iter_entry_points()遍历并将其加入 INSTALLED_APPS tuple 中
外装插件注册:插件的 setup.py 里执行注册 entry_points 的过程:

例:https://github.com/getsentry/sentry-groveio/blob/master/setup.py
参考资料:https://pythonhosted.org/setuptools/pkg_resources.html

#!/usr/bin/env python
import pkg_resources

for ep in pkg_resources.iter_entry_points('sentry.apps'):
    print str(ep)

for ep in pkg_resources.iter_entry_points('sentry.plugins'):
    print str(ep)

sentry-jira 插件

插件基本配置

每 Project 分别配置,需要输入 jira 的 instance URL、用户名和密码。以上内容 base64 存在数据库 sentry_projectoptions 表里 where `key` like ‘jira%’
配置用户名密码之后,选择 Sentry project 关联到哪个 JIRA Project,保存设置,并 Enable Plugin 即可。

Due Date 问题

在 Sentry Event 页面右边点击 Create JIRA Issue 进入创建页面。但下面 Due Date 总提示 Operation value must be a string 。
经 tcpdump,插件发送格式为

"duedate": {"id": "2015-07-31"}

根据 /rest/api/2/issue/createmeta?projectKeys=<我们的KEY>&expand=projects.issuetypes.fields 的说明,duedate 的 schema 为

"duedate": {
    "hasDefaultValue": false,
    "name": "Due Date",
    "operations": [
        "set"
    ],
    "required": true,
    "schema": {
        "system": "duedate",
        "type": "date"
    }
}

根据网上流传的消息(https://answers.atlassian.com/questions/319018/jira-eclipse-mylyn-transition-fails-when-date-is-required-french-format#comment-319464),Jira期望的格式应该是

"duedate":"2014-07-23"

发送格式和期望格式的不同导致了无法创建 Issue。不过 sentry-jira 插件创建的内容是 id,而上述 bugreport 中是 name,也不太一样。
倒推检查插件源代码,发现从 Form 提交到 jira 客户端对象的数据就已经是u’duedate’: {‘id’: u’2015-07-31′} 了
sentry_jira:forms.py里

elif (schema.get("type") != "string"
  or schema.get("item") != "string"
  or schema.get("custom") == CUSTOM_FIELD_TYPES.get("select")):
    v = {"id": v}

这一段缺乏对 date 类型的 Jira 字段的特殊处理,导致前面多加了 id。
也就是说,如果不改 sentry-jira 插件,就无解。所以我提交了pull request https://github.com/thurloat/sentry-jira/pull/71
5日下午突然发现,目前线上 Sentry 6.4.4 的 duedate 显示为 django 的 SelectDateWidget,而不像我自己的测试装 Sentry 7.7 一样直接用文本框。经过仔细对比,发现服务器上的 sentry-jira 插件是修改过的版本,forms.py 文件class JIRAIssueForm 新增了一段

duedate = forms.DateField(
    label="duedate",
    #widget=adminwidgets.AdminDateWidget(),
    widget=SelectDateWidget(),
    initial=datetime.datetime.now(),
    #widget=DateWidget(usel10n=True)
    required=True
)

但这段代码没在公司的git库里保存。

SSO集成

厂家SSO讨论:https://github.com/getsentry/sentry/issues/1372
新版Sentry有 auth backend 基类https://github.com/getsentry/sentry/tree/master/src/sentry/auth/
SENTRY_FEATURES[‘organizations:sso’]改为 True 可以开启 Auth 页面,设置sso。
然后我就参考 sentry-sso-google 写了一个插件。

SENTRY_SINGLE_ORGANIZATION=True 会导致 /auth/login/ 跳转到 /auth/login/org_slug/ ,从而无法登录 非 SSO 的用户(如系统自带的名为 sentry 的超级用户)

Organizations

当 SENTRY_SINGLE_ORGANIZATION=False 时,utils/runner.py加载配置之后会将 SENTRY_FEATURES[‘organizations:create’] 强制改为 False,从而禁用了右上角新 创建 Organization 的“加号”链接。如果在此状态下删除了最后一个 Organization,则其中的  Team会变成游离状态,只能改掉参数重启服务重新创建了,而且重建之后某种情况下会导致游离状态的 Team 丢失。切记不要删除 Organization!!
根据 getsentry 原厂GH-1372号 issue,每个 Organization 只能开启一个 SSO AuthProvider

Posted in 默认分类 | Tagged , | Leave a comment

某种情况下PATH不生效的问题

今天遇到的情况,我同事在 virtualenv 里执行 gunicorn 运行 Django app,出来的 Django 错误信息却是 /usr/local/ 路径下面那套 Python 里的:

2015-09-18 15:32:47 [11538] [ERROR] Error handling request
Traceback (most recent call last):
File “/usr/local/lib/python2.7/dist-packages/gunicorn/workers/sync.py“, line 131, in handle_request
respiter = self.wsgi(environ, resp.start_response)
File “/usr/local/lib/python2.7/dist-packages/django/core/handlers/wsgi.py“, line 236, in __call__
self.load_middleware()
File “/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py“, line 57, in load_middleware
raise exceptions.ImproperlyConfigured(‘Middleware module “%s” does not define a “%s” class’ % (mw_module, mw_classname))
ImproperlyConfigured: Middleware module “django.contrib.auth.middleware” does not define a “SessionAuthenticationMiddleware” class

尝试 which gunicorn 返回结果是 virtualenv 里的那个 gunicorn;尝试 strace gunicorn 执行,却能正常执行。

我试了试,把 /usr/local/bin/gunicorn 的x权限取消,再让同事执行,就返回 Permission denied 说明执行的不是 Virtualenv 里的 gunicorn。改试 type gunicorn 返回结果:

gunicorn is hashed (/usr/local/bin/gunicorn)

目前尚不清楚是什么原因造成了该命令被 bash 给 hash 了。source virtualenv的 activate 命令的时候 hash 表会被清空的。

Posted in 默认分类 | Tagged , , | Leave a comment