转载自:[记录] Open edX 配置 CAS 登陆

Open edX 目前支持CAS、SSL Client Certificates、Shibboleth三种认证扩展以及多种社会化登录(博客先前有过介绍),
默认支持列表可以参照官方WIki

CAS干嘛用的,看官比我清楚,没我清楚的请立即谷歌,故下面直接操作:

1.环境:

ubuntu 12.04
edx-devstack

      <td class="content">
        <code class="functions">sudo</code> <code class="functions">su</code> <code class="plain">edxapp</code>
      </td>
    </tr>
  </table>
</div>

2.更换django-cas

edX默认安装mitx的版本base.txt#L145略旧,我们需要换掉

      <td class="content">
        <code class="plain">pip unstall django-cas</code>
      </td>
    </tr>
  </table>
</div>

请先确认您已经安装mercurial

      <td class="content">
        <code class="functions">sudo</code> <code class="plain">apt-get </code><code class="functions">install</code> <code class="plain">mercurial</code>
      </td>
    </tr>
  </table>
</div>

克隆cpcc的最新版本:

      <td class="content">
        <code class="plain">hg clone &lt;a href="https://bitbucket.org/cpcc/django-cas">https://bitbucket.org/cpcc/django-cas&lt;/a></code>
      </td>
    </tr>
  </table>
</div>

3.CAS Server要求:

你可以安装官方标准CAS 1.x 2.x 或者3.X理论上都支持,但必须保证CAS提供username 和 email信息,建议不要使用1.x,推荐3.x
3.x添加别的字段可以参考官方wiki配置
2.x默认只提供username,需要添加别的信息就要硬编码,然后django-cas也需要相应的修改
我们测试使用2.x,只是因为学校请的攻城狮在闭源的道路上越走越远,楼主挽救不了。。。苦恼啊

4.Django-cas修改:

编辑 django_cas/backends.py 修改方法_verify_cas2

      <td class="content">
        <code class="keyword">def</code> <code class="plain">_verify_cas2(ticket, service):</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>2</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="comments">"""Verifies CAS 2.0+ XML-based authentication ticket.</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>3</code>
      </td>
      
      <td class="content">
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>4</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="comments">Returns username on success and None on failure.</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>5</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="comments">"""</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>6</code>
      </td>
      
      <td class="content">
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>7</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="keyword">try</code><code class="plain">:</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>8</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="keyword">from</code> <code class="plain">xml.etree </code><code class="keyword">import</code> <code class="plain">ElementTree</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>9</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="keyword">except</code> <code class="plain">ImportError:</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>10</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="keyword">from</code> <code class="plain">elementtree </code><code class="keyword">import</code> <code class="plain">ElementTree</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>11</code>
      </td>
      
      <td class="content">
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>12</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="plain">params </code><code class="keyword">=</code> <code class="plain">{</code><code class="string">'ticket'</code><code class="plain">: ticket, </code><code class="string">'service'</code><code class="plain">: service}</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>13</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="plain">url </code><code class="keyword">=</code> <code class="plain">(urljoin(settings.CAS_SERVER_URL, </code><code class="string">'proxyValidate'</code><code class="plain">) </code><code class="keyword">+</code> <code class="string">'?'</code> <code class="keyword">+</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>14</code>
      </td>
      
      <td class="content">
        <code class="spaces">           </code><code class="plain">urlencode(params))</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>15</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="plain">page </code><code class="keyword">=</code> <code class="plain">urlopen(url)</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>16</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="keyword">try</code><code class="plain">:</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>17</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="plain">response </code><code class="keyword">=</code> <code class="plain">page.read()</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>18</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="plain">elements </code><code class="keyword">=</code> <code class="plain">{}</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>19</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="plain">tree </code><code class="keyword">=</code> <code class="plain">ElementTree.fromstring(response)</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>20</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="keyword">if</code> <code class="plain">tree[</code><code class="value"></code><code class="plain">].tag.endswith(</code><code class="string">'authenticationSuccess'</code><code class="plain">):</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>21</code>
      </td>
      
      <td class="content">
        <code class="spaces">            </code><code class="keyword">for</code> <code class="plain">element </code><code class="keyword">in</code> <code class="plain">tree[</code><code class="value"></code><code class="plain">]:</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>22</code>
      </td>
      
      <td class="content">
        <code class="spaces">                    </code><code class="plain">elements[element.tag.split(</code><code class="string">"}"</code><code class="plain">).pop()] </code><code class="keyword">=</code> <code class="plain">element.text</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>23</code>
      </td>
      
      <td class="content">
        <code class="spaces">            </code><code class="keyword">return</code> <code class="plain">tree[</code><code class="value"></code><code class="plain">][</code><code class="value"></code><code class="plain">].text, elements</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>24</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="keyword">else</code><code class="plain">:</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>25</code>
      </td>
      
      <td class="content">
        <code class="spaces">            </code><code class="keyword">return</code> <code class="color1">None</code><code class="plain">, </code><code class="color1">None</code>
      </td>
    </tr>
  </table>
</div>

然后修改CASBackend(object)类中的authenticate方法:

      <td class="content">
        <code class="keyword">def</code> <code class="plain">authenticate(</code><code class="color1">self</code><code class="plain">, ticket, service, request):</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>2</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="comments">"""Verifies CAS ticket and gets or creates User object"""</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>3</code>
      </td>
      
      <td class="content">
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>4</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="plain">username, attributes </code><code class="keyword">=</code> <code class="plain">_verify(ticket, service)</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>5</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="keyword">if</code> <code class="plain">attributes:</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>6</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="plain">request.session[</code><code class="string">'attributes'</code><code class="plain">] </code><code class="keyword">=</code> <code class="plain">attributes</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>7</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="keyword">if</code> <code class="keyword">not</code> <code class="plain">username:</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>8</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="keyword">return</code> <code class="color1">None</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>9</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="keyword">try</code><code class="plain">:</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>10</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="plain">user </code><code class="keyword">=</code> <code class="plain">User.objects.get(username</code><code class="keyword">=</code><code class="plain">username)</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>11</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="keyword">except</code> <code class="plain">User.DoesNotExist:</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>12</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="comments"># user will have an "unusable" password</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>13</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="plain">user </code><code class="keyword">=</code> <code class="plain">User.objects.create_user(username,attributes[</code><code class="string">'email'</code><code class="plain">],'')</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>14</code>
      </td>
      
      <td class="content">
        <code class="spaces">        </code><code class="plain">user.save()</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>15</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="keyword">return</code> <code class="plain">user</code>
      </td>
    </tr>
  </table>
</div>

然后安装:

      <td class="content">
        <code class="plain">python django_cas/setup.py </code><code class="functions">install</code>
      </td>
    </tr>
  </table>
</div>

5.配置Open edX:

编辑 edx-platform/lms/envs/devstack.py 加入:

      <td class="content">
        <code class="plain">FEATURES[</code><code class="string">'AUTH_USE_CAS'</code><code class="plain">] = True</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>2</code>
      </td>
      
      <td class="content">
        <code class="plain">CAS_SERVER_URL = </code><code class="string">"&lt;a href="http://cas/login">http://cas/login&lt;/a>"</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>3</code>
      </td>
      
      <td class="content">
        <code class="plain">AUTHENTICATION_BACKENDS = (</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>4</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="string">'django.contrib.auth.backends.ModelBackend'</code><code class="plain">,</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>5</code>
      </td>
      
      <td class="content">
        <code class="spaces">    </code><code class="string">'django_cas.backends.CASBackend'</code><code class="plain">,</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>6</code>
      </td>
      
      <td class="content">
        <code class="plain">)</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>7</code>
      </td>
      
      <td class="content">
        <code class="plain">INSTALLED_APPS += (</code><code class="string">'django_cas'</code><code class="plain">,)</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>8</code>
      </td>
      
      <td class="content">
        <code class="plain">MIDDLEWARE_CLASSES += (</code><code class="string">'django_cas.middleware.CASMiddleware'</code><code class="plain">,)</code>
      </td>
    </tr>
  </table>
</div>

6.测试:

      <td class="content">
        <code class="functions">cd</code> <code class="plain">edx-platform</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt2">
  <table>
    <tr>
      <td class="number">
        <code>2</code>
      </td>
      
      <td class="content">
        <code class="plain">paver update_assets</code>
      </td>
    </tr>
  </table>
</div>

<div class="line alt1">
  <table>
    <tr>
      <td class="number">
        <code>3</code>
      </td>
      
      <td class="content">
        <code class="plain">paver devstack lms</code>
      </td>
    </tr>
  </table>
</div>