Compare commits
	
		
			14 Commits
		
	
	
		
			version/0.
			...
			version/0.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7e9154a0ea | |||
| e0ef061771 | |||
| b8694a7ade | |||
| 10d6a30f2c | |||
| 8c94aef6d0 | |||
| 19bd3bfffb | |||
| 8611ac624c | |||
| fa93b59a8c | |||
| 8b66b40f0d | |||
| c2756f15fc | |||
| 408e205c5f | |||
| 5f3ab49535 | |||
| 33431ae013 | |||
| b40ac6dc5d | 
| @ -1,5 +1,5 @@ | ||||
| [bumpversion] | ||||
| current_version = 0.0.7-alpha | ||||
| current_version = 0.0.8-alpha | ||||
| tag = True | ||||
| commit = True | ||||
| parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*) | ||||
|  | ||||
| @ -54,7 +54,7 @@ package-docker: | ||||
|   before_script: | ||||
|     - echo "{\"auths\":{\"https://docker.$NEXUS_URL/\":{\"username\":\"$NEXUS_USER\",\"password\":\"$NEXUS_PASS\"}}}" > /kaniko/.docker/config.json | ||||
|   script: | ||||
|     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.0.7-alpha | ||||
|     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.0.8-alpha | ||||
|   stage: build | ||||
|   only: | ||||
|     - tags | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| """passbook provider""" | ||||
| from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns | ||||
|  | ||||
| from allauth_passbook.provider import PassbookProvider | ||||
|  | ||||
| urlpatterns = default_urlpatterns(PassbookProvider) | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| """passbook adapter""" | ||||
| import requests | ||||
|  | ||||
| from allauth.socialaccount import app_settings | ||||
| from allauth.socialaccount.providers.oauth2.views import (OAuth2Adapter, | ||||
|                                                           OAuth2CallbackView, | ||||
|                                                           OAuth2LoginView) | ||||
|  | ||||
| from allauth_passbook.provider import PassbookProvider | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| apiVersion: v1 | ||||
| appVersion: "0.0.7-alpha" | ||||
| appVersion: "0.0.8-alpha" | ||||
| description: A Helm chart for passbook. | ||||
| name: passbook | ||||
| version: 1.0.0 | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook admin""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
| @ -9,7 +9,7 @@ | ||||
|  | ||||
| {% block content %} | ||||
| <div class="container"> | ||||
|   <h1>{% trans "Applications" %}</h1> | ||||
|     <h1><span class="pficon-applications"></span> {% trans "Applications" %}</h1> | ||||
|     <span>{% trans "External Applications which use passbook as Identity-Provider, utilizing protocols like OAuth2 and SAML." %}</span> | ||||
|     <hr> | ||||
|     <a href="{% url 'passbook_admin:application-create' %}" class="btn btn-primary"> | ||||
| @ -30,8 +30,10 @@ | ||||
|                 <td>{{ application.name }}</td> | ||||
|                 <td>{{ application.provider }}</td> | ||||
|                 <td> | ||||
|             <a class="btn btn-default btn-sm" href="{% url 'passbook_admin:application-update' pk=application.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> | ||||
|             <a class="btn btn-default btn-sm" href="{% url 'passbook_admin:application-delete' pk=application.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{% url 'passbook_admin:application-update' pk=application.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{% url 'passbook_admin:application-delete' pk=application.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a> | ||||
|                 </td> | ||||
|             </tr> | ||||
|             {% endfor %} | ||||
|  | ||||
| @ -8,8 +8,8 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
|   <h1>{% trans "Audit Log" %}</h1> | ||||
|   <div id="pf-list-standard" class="list-group list-view-pf list-view-pf-view"> | ||||
| <h1><span class="pficon-catalog"></span> {% trans "Audit Log" %}</h1> | ||||
| <div id="pf-list-standard" class="list-group list-view-pf list-view-pf-view"> | ||||
|     {% for entry in object_list %} | ||||
|     <div class="list-group-item"> | ||||
|         <div class="list-view-pf-main-info"> | ||||
|  | ||||
| @ -10,7 +10,7 @@ | ||||
|  | ||||
| {% block content %} | ||||
| <div class="container"> | ||||
|     <h1>{% trans "Factors" %}</h1> | ||||
|     <h1><span class="pficon-plugged"></span> {% trans "Factors" %}</h1> | ||||
|     <span>{% trans "Factors required for a user to successfully authenticate." %}</span> | ||||
|     <hr> | ||||
|     <div class="dropdown"> | ||||
| @ -20,7 +20,8 @@ | ||||
|         </button> | ||||
|         <ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown"> | ||||
|             {% for type, name in types.items %} | ||||
|             <li role="presentation"><a role="menuitem" tabindex="-1" href="{% url 'passbook_admin:factor-create' %}?type={{ type }}">{{ name }}</a></li> | ||||
|             <li role="presentation"><a role="menuitem" tabindex="-1" | ||||
|                     href="{% url 'passbook_admin:factor-create' %}?type={{ type }}">{{ name }}</a></li> | ||||
|             {% endfor %} | ||||
|         </ul> | ||||
|     </div> | ||||
|  | ||||
| @ -9,7 +9,7 @@ | ||||
|  | ||||
| {% block content %} | ||||
| <div class="container"> | ||||
|   <h1>{% trans "Invitations" %}</h1> | ||||
|     <h1><span class="pficon-migration"></span> {% trans "Invitations" %}</h1> | ||||
|     <span>{% trans "Create Invitation Links which optionally force a username or expire on a set date." %}</span> | ||||
|     <hr> | ||||
|     <a href="{% url 'passbook_admin:invitation-create' %}" class="btn btn-primary"> | ||||
| @ -28,9 +28,12 @@ | ||||
|             {% for invitation in object_list %} | ||||
|             <tr> | ||||
|                 <td>{{ invitation.expires|default:"Never" }}</td> | ||||
|           <td><pre>{{ invitation.link }}</pre></td> | ||||
|                 <td> | ||||
|             <a class="btn btn-default btn-sm" href="{% url 'passbook_admin:invitation-delete' pk=invitation.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a> | ||||
|                     <pre>{{ invitation.link }}</pre> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{% url 'passbook_admin:invitation-delete' pk=invitation.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a> | ||||
|                 </td> | ||||
|             </tr> | ||||
|             {% endfor %} | ||||
|  | ||||
| @ -7,11 +7,18 @@ | ||||
|     <div class="col-xs-6 col-sm-2 col-md-2"> | ||||
|         <div class="card-pf card-pf-accented card-pf-aggregate-status"> | ||||
|             <h2 class="card-pf-title"> | ||||
|                 <a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Applications' %}</a> | ||||
|                 <a href="{% url 'passbook_admin:applications' %}"> | ||||
|                     <span class="pficon-applications"></span> | ||||
|                     <span class="card-pf-aggregate-status-count"></span> {% trans 'Applications' %} | ||||
|                 </a> | ||||
|             </h2> | ||||
|             <div class="card-pf-body"> | ||||
|                 <p class="card-pf-aggregate-status-notifications"> | ||||
|                     <span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ application_count }}</a></span> | ||||
|                     <span class="card-pf-aggregate-status-notification"> | ||||
|                         <a href="{% url 'passbook_admin:applications' %}"> | ||||
|                             <span class="pficon pficon-ok"></span>{{ application_count }} | ||||
|                         </a> | ||||
|                     </span> | ||||
|                 </p> | ||||
|             </div> | ||||
|         </div> | ||||
| @ -19,11 +26,18 @@ | ||||
|     <div class="col-xs-6 col-sm-2 col-md-2"> | ||||
|         <div class="card-pf card-pf-accented card-pf-aggregate-status"> | ||||
|             <h2 class="card-pf-title"> | ||||
|                 <a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Sources' %}</a> | ||||
|                 <a href="{% url 'passbook_admin:sources' %}"> | ||||
|                     <span class="pficon-resource-pool"></span> | ||||
|                     <span class="card-pf-aggregate-status-count"></span> {% trans 'Sources' %} | ||||
|                 </a> | ||||
|             </h2> | ||||
|             <div class="card-pf-body"> | ||||
|                 <p class="card-pf-aggregate-status-notifications"> | ||||
|                     <span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ source_count }}</a></span> | ||||
|                     <span class="card-pf-aggregate-status-notification"> | ||||
|                         <a href="{% url 'passbook_admin:sources' %}"> | ||||
|                             <span class="pficon pficon-ok"></span>{{ source_count }} | ||||
|                         </a> | ||||
|                     </span> | ||||
|                 </p> | ||||
|             </div> | ||||
|         </div> | ||||
| @ -31,11 +45,18 @@ | ||||
|     <div class="col-xs-6 col-sm-2 col-md-2"> | ||||
|         <div class="card-pf card-pf-accented card-pf-aggregate-status"> | ||||
|             <h2 class="card-pf-title"> | ||||
|                 <a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Providers' %}</a> | ||||
|                 <a href="{% url 'passbook_admin:providers' %}"> | ||||
|                     <span class="pficon-integration"></span> | ||||
|                     <span class="card-pf-aggregate-status-count"></span> {% trans 'Providers' %} | ||||
|                 </a> | ||||
|             </h2> | ||||
|             <div class="card-pf-body"> | ||||
|                 <p class="card-pf-aggregate-status-notifications"> | ||||
|                     <span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ provider_count }}</a></span> | ||||
|                     <span class="card-pf-aggregate-status-notification"> | ||||
|                         <a href="{% url 'passbook_admin:providers' %}"> | ||||
|                             <span class="pficon pficon-ok"></span>{{ provider_count }} | ||||
|                         </a> | ||||
|                     </span> | ||||
|                 </p> | ||||
|             </div> | ||||
|         </div> | ||||
| @ -43,11 +64,18 @@ | ||||
|     <div class="col-xs-6 col-sm-2 col-md-2"> | ||||
|         <div class="card-pf card-pf-accented card-pf-aggregate-status"> | ||||
|             <h2 class="card-pf-title"> | ||||
|                 <a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Factors' %}</a> | ||||
|                 <a href="{% url 'passbook_admin:factors' %}"> | ||||
|                     <span class="pficon-plugged"></span> | ||||
|                     <span class="card-pf-aggregate-status-count"></span> {% trans 'Factors' %} | ||||
|                 </a> | ||||
|             </h2> | ||||
|             <div class="card-pf-body"> | ||||
|                 <p class="card-pf-aggregate-status-notifications"> | ||||
|                     <span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ factor_count }}</a></span> | ||||
|                     <span class="card-pf-aggregate-status-notification"> | ||||
|                         <a href="{% url 'passbook_admin:factors' %}"> | ||||
|                             <span class="pficon pficon-ok"></span>{{ factor_count }} | ||||
|                         </a> | ||||
|                     </span> | ||||
|                 </p> | ||||
|             </div> | ||||
|         </div> | ||||
| @ -55,11 +83,18 @@ | ||||
|     <div class="col-xs-6 col-sm-2 col-md-2"> | ||||
|         <div class="card-pf card-pf-accented card-pf-aggregate-status"> | ||||
|             <h2 class="card-pf-title"> | ||||
|                 <a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Invitation' %}</a> | ||||
|                 <a href="{% url 'passbook_admin:policies' %}"> | ||||
|                     <span class="pficon-infrastructure"></span> | ||||
|                     <span class="card-pf-aggregate-status-count"></span> {% trans 'Policies' %} | ||||
|                 </a> | ||||
|             </h2> | ||||
|             <div class="card-pf-body"> | ||||
|                 <p class="card-pf-aggregate-status-notifications"> | ||||
|                     <span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ invitation_count }}</a></span> | ||||
|                     <span class="card-pf-aggregate-status-notification"> | ||||
|                         <a href="{% url 'passbook_admin:policies' %}"> | ||||
|                             <span class="pficon pficon-ok"></span>{{ policy_count }} | ||||
|                         </a> | ||||
|                     </span> | ||||
|                 </p> | ||||
|             </div> | ||||
|         </div> | ||||
| @ -67,11 +102,18 @@ | ||||
|     <div class="col-xs-6 col-sm-2 col-md-2"> | ||||
|         <div class="card-pf card-pf-accented card-pf-aggregate-status"> | ||||
|             <h2 class="card-pf-title"> | ||||
|                 <a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Policies' %}</a> | ||||
|                 <a href="{% url 'passbook_admin:invitations' %}"> | ||||
|                     <span class="pficon-migration"></span> | ||||
|                     <span class="card-pf-aggregate-status-count"></span> {% trans 'Invitation' %} | ||||
|                 </a> | ||||
|             </h2> | ||||
|             <div class="card-pf-body"> | ||||
|                 <p class="card-pf-aggregate-status-notifications"> | ||||
|                     <span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ policy_count }}</a></span> | ||||
|                     <span class="card-pf-aggregate-status-notification"> | ||||
|                         <a href="{% url 'passbook_admin:invitations' %}"> | ||||
|                             <span class="pficon pficon-ok"></span>{{ invitation_count }} | ||||
|                         </a> | ||||
|                     </span> | ||||
|                 </p> | ||||
|             </div> | ||||
|         </div> | ||||
| @ -79,11 +121,18 @@ | ||||
|     <div class="col-xs-6 col-sm-2 col-md-2"> | ||||
|         <div class="card-pf card-pf-accented card-pf-aggregate-status"> | ||||
|             <h2 class="card-pf-title"> | ||||
|                 <a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Users' %}</a> | ||||
|                 <a href="{% url 'passbook_admin:users' %}"> | ||||
|                     <span class="pficon-users"></span> | ||||
|                     <span class="card-pf-aggregate-status-count"></span> {% trans 'Users' %} | ||||
|                 </a> | ||||
|             </h2> | ||||
|             <div class="card-pf-body"> | ||||
|                 <p class="card-pf-aggregate-status-notifications"> | ||||
|                     <span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ user_count }}</a></span> | ||||
|                     <span class="card-pf-aggregate-status-notification"> | ||||
|                         <a href="{% url 'passbook_admin:users' %}"> | ||||
|                             <span class="pficon pficon-ok"></span>{{ user_count }} | ||||
|                         </a> | ||||
|                     </span> | ||||
|                 </p> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
| @ -9,7 +9,7 @@ | ||||
|  | ||||
| {% block content %} | ||||
| <div class="container"> | ||||
|   <h1>{% trans "Policies" %}</h1> | ||||
|     <h1><span class="pficon-infrastructure"></span> {% trans "Policies" %}</h1> | ||||
|     <span>{% trans "Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Factors." %}</span> | ||||
|     <hr> | ||||
|     <div class="dropdown"> | ||||
| @ -19,7 +19,8 @@ | ||||
|         </button> | ||||
|         <ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown"> | ||||
|             {% for type, name in types.items %} | ||||
|       <li role="presentation"><a role="menuitem" tabindex="-1" href="{% url 'passbook_admin:policy-create' %}?type={{ type }}">{{ name }}</a></li> | ||||
|             <li role="presentation"><a role="menuitem" tabindex="-1" | ||||
|                     href="{% url 'passbook_admin:policy-create' %}?type={{ type }}">{{ name }}</a></li> | ||||
|             {% endfor %} | ||||
|         </ul> | ||||
|     </div> | ||||
| @ -38,9 +39,12 @@ | ||||
|                 <td>{{ policy.name }}</td> | ||||
|                 <td>{{ policy|fieldtype }}</td> | ||||
|                 <td> | ||||
|             <a class="btn btn-default btn-sm" href="{% url 'passbook_admin:policy-update' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> | ||||
|             <a class="btn btn-default btn-sm" href="{% url 'passbook_admin:policy-test' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Test' %}</a> | ||||
|             <a class="btn btn-default btn-sm" href="{% url 'passbook_admin:policy-delete' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{% url 'passbook_admin:policy-update' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{% url 'passbook_admin:policy-test' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Test' %}</a> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{% url 'passbook_admin:policy-delete' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a> | ||||
|                 </td> | ||||
|             </tr> | ||||
|             {% endfor %} | ||||
|  | ||||
| @ -10,7 +10,7 @@ | ||||
|  | ||||
| {% block content %} | ||||
| <div class="container"> | ||||
|   <h1>{% trans "Providers" %}</h1> | ||||
|     <h1><span class="pficon-integration"></span> {% trans "Providers" %}</h1> | ||||
|     <span>{% trans "Authentication Protocol Provider, used as Protocol behind an Application." %}</span> | ||||
|     <hr> | ||||
|     <div class="dropdown"> | ||||
| @ -20,7 +20,8 @@ | ||||
|         </button> | ||||
|         <ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown"> | ||||
|             {% for type, name in types.items %} | ||||
|       <li role="presentation"><a role="menuitem" tabindex="-1" href="{% url 'passbook_admin:provider-create' %}?type={{ type }}">{{ name }}</a></li> | ||||
|             <li role="presentation"><a role="menuitem" tabindex="-1" | ||||
|                     href="{% url 'passbook_admin:provider-create' %}?type={{ type }}">{{ name }}</a></li> | ||||
|             {% endfor %} | ||||
|         </ul> | ||||
|     </div> | ||||
| @ -39,11 +40,14 @@ | ||||
|                 <td>{{ provider.name }}</td> | ||||
|                 <td>{{ provider|fieldtype }}</td> | ||||
|                 <td> | ||||
|             <a class="btn btn-default btn-sm" href="{% url 'passbook_admin:provider-update' pk=provider.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> | ||||
|             <a class="btn btn-default btn-sm" href="{% url 'passbook_admin:provider-delete' pk=provider.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{% url 'passbook_admin:provider-update' pk=provider.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{% url 'passbook_admin:provider-delete' pk=provider.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a> | ||||
|                     {% get_links provider as links %} | ||||
|                     {% for name, href in links.items %} | ||||
|               <a class="btn btn-default btn-sm" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a> | ||||
|                     {% endfor %} | ||||
|                 </td> | ||||
|             </tr> | ||||
|  | ||||
| @ -6,7 +6,7 @@ | ||||
|  | ||||
| {% block content %} | ||||
| <div class="container"> | ||||
|     <h1>{% trans "Sources" %}</h1> | ||||
|     <h1><span class="pficon-resource-pool"></span> {% trans "Sources" %}</h1> | ||||
|     <span>{% trans "External Sources which can be used to get Identities into passbook, for example Social Providers like Twiter and GitHub or Enterprise Providers like ADFS and LDAP." %}</span> | ||||
|     <hr> | ||||
|     <div class="dropdown"> | ||||
| @ -27,6 +27,7 @@ | ||||
|             <tr> | ||||
|                 <th>{% trans 'Name' %}</th> | ||||
|                 <th>{% trans 'Class' %}</th> | ||||
|                 <th>{% trans 'Additional Info' %}</th> | ||||
|                 <th></th> | ||||
|             </tr> | ||||
|         </thead> | ||||
| @ -35,6 +36,7 @@ | ||||
|             <tr> | ||||
|                 <td>{{ source.name }}</td> | ||||
|                 <td>{{ source|fieldtype }}</td> | ||||
|                 <td>{{ source.additional_info }}</td> | ||||
|                 <td> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{% url 'passbook_admin:source-update' pk=source.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|  | ||||
| {% block content %} | ||||
| <div class="container"> | ||||
|   <h1>{% trans "Users" %}</h1> | ||||
|     <h1><span class="pficon-users"></span> {% trans "Users" %}</h1> | ||||
|     <hr> | ||||
|     <table class="table table-striped table-bordered"> | ||||
|         <thead> | ||||
| @ -27,8 +27,10 @@ | ||||
|                 <td>{{ user.is_active }}</td> | ||||
|                 <td>{{ user.last_login }}</td> | ||||
|                 <td> | ||||
|             <a class="btn btn-default btn-sm" href="{% url 'passbook_admin:user-update' pk=user.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> | ||||
|             <a class="btn btn-default btn-sm" href="{% url 'passbook_admin:user-delete' pk=user.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{% url 'passbook_admin:user-update' pk=user.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> | ||||
|                     <a class="btn btn-default btn-sm" | ||||
|                         href="{% url 'passbook_admin:user-delete' pk=user.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a> | ||||
|                 </td> | ||||
|             </tr> | ||||
|             {% endfor %} | ||||
|  | ||||
| @ -5,3 +5,7 @@ | ||||
| {% block above_form %} | ||||
| <h1>{% trans 'Create' %}</h1> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block action %} | ||||
| {% trans 'Create' %} | ||||
| {% endblock %} | ||||
|  | ||||
| @ -11,7 +11,7 @@ | ||||
|     <form action="" method="post" class="form-horizontal"> | ||||
|       {% include 'partials/form.html' with form=form %} | ||||
|       <a class="btn btn-default" href="{% back %}">{% trans "Cancel" %}</a> | ||||
|       <input type="submit" class="btn btn-primary" value="{% trans 'Create' %}" /> | ||||
|       <input type="submit" class="btn btn-primary" value="{% block action %}{% endblock %}" /> | ||||
|     </form> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| @ -5,3 +5,7 @@ | ||||
| {% block above_form %} | ||||
| <h1>{% trans 'Update' %}</h1> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block action %} | ||||
| {% trans 'Update' %} | ||||
| {% endblock %} | ||||
|  | ||||
| @ -39,19 +39,18 @@ class FactorCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): | ||||
|  | ||||
|     def get_context_data(self, **kwargs): | ||||
|         kwargs = super().get_context_data(**kwargs) | ||||
|         source_type = self.request.GET.get('type') | ||||
|         model = next(x for x in all_subclasses(Factor) if x.__name__ == source_type) | ||||
|         factor_type = self.request.GET.get('type') | ||||
|         model = next(x for x in all_subclasses(Factor) if x.__name__ == factor_type) | ||||
|         kwargs['type'] = model._meta.verbose_name | ||||
|         return kwargs | ||||
|  | ||||
|     def get_form_class(self): | ||||
|         source_type = self.request.GET.get('type') | ||||
|         model = next(x for x in all_subclasses(Factor) if x.__name__ == source_type) | ||||
|         factor_type = self.request.GET.get('type') | ||||
|         model = next(x for x in all_subclasses(Factor) if x.__name__ == factor_type) | ||||
|         if not model: | ||||
|             raise Http404 | ||||
|         return path_to_class(model.form) | ||||
|  | ||||
|  | ||||
| class FactorUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView): | ||||
|     """Update factor""" | ||||
|  | ||||
| @ -61,11 +60,12 @@ class FactorUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView): | ||||
|     success_message = _('Successfully updated Factor') | ||||
|  | ||||
|     def get_form_class(self): | ||||
|         source_type = self.request.GET.get('type') | ||||
|         model = next(x for x in all_subclasses(Factor) if x.__name__ == source_type) | ||||
|         if not model: | ||||
|             raise Http404 | ||||
|         return path_to_class(model.form) | ||||
|         form_class_path = self.get_object().form | ||||
|         form_class = path_to_class(form_class_path) | ||||
|         return form_class | ||||
|  | ||||
|     def get_object(self, queryset=None): | ||||
|         return Factor.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first() | ||||
|  | ||||
| class FactorDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView): | ||||
|     """Delete factor""" | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook api""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook audit Header""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
| @ -84,6 +84,8 @@ class LoginAttempt(CreatedUpdatedModel): | ||||
|     @staticmethod | ||||
|     def attempt(target_uid, request): | ||||
|         """Helper function to create attempt or count up existing one""" | ||||
|         if not target_uid: | ||||
|             return | ||||
|         client_ip, _ = get_client_ip(request) | ||||
|         # Since we can only use 254 chars for target_uid, truncate target_uid. | ||||
|         target_uid = target_uid[:254] | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook captcha_factor Header""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook core""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
| @ -1,9 +1,11 @@ | ||||
| """passbook multi-factor authentication engine""" | ||||
| from logging import getLogger | ||||
|  | ||||
| from django.contrib import messages | ||||
| from django.contrib.auth import authenticate | ||||
| from django.core.exceptions import PermissionDenied | ||||
| from django.forms.utils import ErrorList | ||||
| from django.shortcuts import redirect | ||||
| from django.utils.translation import gettext as _ | ||||
| from django.views.generic import FormView | ||||
|  | ||||
| @ -21,6 +23,19 @@ class PasswordFactor(FormView, AuthenticationFactor): | ||||
|     form_class = PasswordFactorForm | ||||
|     template_name = 'login/factors/backend.html' | ||||
|  | ||||
|     def get_context_data(self, **kwargs): | ||||
|         kwargs['show_password_forget_notice'] = CONFIG.y('passbook.password_reset.enabled') | ||||
|         return super().get_context_data(**kwargs) | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         if 'password-forgotten' in request.GET: | ||||
|             # TODO: Save nonce key in database for password reset | ||||
|             # TODO: Send email to user | ||||
|             self.authenticator.cleanup() | ||||
|             messages.success(request, _('Check your E-Mails for a password reset link.')) | ||||
|             return redirect('passbook_core:auth-login') | ||||
|         return super().get(request, *args, **kwargs) | ||||
|  | ||||
|     def form_valid(self, form): | ||||
|         """Authenticate against django's authentication backend""" | ||||
|         uid_fields = CONFIG.y('passbook.uid_fields') | ||||
|  | ||||
| @ -111,7 +111,7 @@ class AuthenticationView(UserPassesTestMixin, View): | ||||
|         """Show error message, user cannot login. | ||||
|         This should only be shown if user authenticated successfully, but is disabled/locked/etc""" | ||||
|         LOGGER.debug("User invalid") | ||||
|         self._cleanup() | ||||
|         self.cleanup() | ||||
|         return redirect(reverse('passbook_core:auth-denied')) | ||||
|  | ||||
|     def _user_passed(self): | ||||
| @ -121,13 +121,13 @@ class AuthenticationView(UserPassesTestMixin, View): | ||||
|         login(self.request, self.pending_user, backend=backend) | ||||
|         LOGGER.debug("Logged in user %s", self.pending_user) | ||||
|         # Cleanup | ||||
|         self._cleanup() | ||||
|         self.cleanup() | ||||
|         next_param = self.request.GET.get('next', None) | ||||
|         if next_param and is_url_absolute(next_param): | ||||
|             return redirect(next_param) | ||||
|         return redirect(reverse('passbook_core:overview')) | ||||
|  | ||||
|     def _cleanup(self): | ||||
|     def cleanup(self): | ||||
|         """Remove temporary data from session""" | ||||
|         session_keys = [self.SESSION_FACTOR, self.SESSION_PENDING_FACTORS, | ||||
|                         self.SESSION_PENDING_USER, self.SESSION_USER_BACKEND, ] | ||||
|  | ||||
| @ -11,7 +11,7 @@ class PasswordFactorForm(forms.ModelForm): | ||||
|     class Meta: | ||||
|  | ||||
|         model = PasswordFactor | ||||
|         fields = GENERAL_FIELDS + ['backends'] | ||||
|         fields = GENERAL_FIELDS + ['backends', 'password_policies'] | ||||
|         widgets = { | ||||
|             'name': forms.TextInput(), | ||||
|             'order': forms.NumberInput(), | ||||
|  | ||||
							
								
								
									
										25
									
								
								passbook/core/migrations/0011_auto_20190225_1438.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								passbook/core/migrations/0011_auto_20190225_1438.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| # Generated by Django 2.1.7 on 2019-02-25 14:38 | ||||
|  | ||||
| import django.utils.timezone | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('passbook_core', '0010_auto_20190224_1016'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='passwordfactor', | ||||
|             name='password_policies', | ||||
|             field=models.ManyToManyField(blank=True, to='passbook_core.Policy'), | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name='user', | ||||
|             name='password_change_date', | ||||
|             field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), | ||||
|             preserve_default=False, | ||||
|         ), | ||||
|     ] | ||||
| @ -9,9 +9,11 @@ from django.contrib.auth.models import AbstractUser | ||||
| from django.contrib.postgres.fields import ArrayField | ||||
| from django.db import models | ||||
| from django.urls import reverse_lazy | ||||
| from django.utils.timezone import now | ||||
| from django.utils.translation import gettext as _ | ||||
| from model_utils.managers import InheritanceManager | ||||
|  | ||||
| from passbook.core.signals import password_changed | ||||
| from passbook.lib.models import CreatedUpdatedModel, UUIDModel | ||||
|  | ||||
| LOGGER = getLogger(__name__) | ||||
| @ -38,6 +40,12 @@ class User(AbstractUser): | ||||
|     sources = models.ManyToManyField('Source', through='UserSourceConnection') | ||||
|     applications = models.ManyToManyField('Application') | ||||
|     groups = models.ManyToManyField('Group') | ||||
|     password_change_date = models.DateTimeField(auto_now_add=True) | ||||
|  | ||||
|     def set_password(self, password): | ||||
|         password_changed.send(sender=self, user=self, password=password) | ||||
|         self.password_change_date = now() | ||||
|         return super().set_password(password) | ||||
|  | ||||
| class Provider(models.Model): | ||||
|     """Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application""" | ||||
| @ -87,6 +95,7 @@ class PasswordFactor(Factor): | ||||
|     """Password-based Django-backend Authentication Factor""" | ||||
|  | ||||
|     backends = ArrayField(models.TextField()) | ||||
|     password_policies = models.ManyToManyField('Policy', blank=True) | ||||
|  | ||||
|     type = 'passbook.core.auth.factors.password.PasswordFactor' | ||||
|     form = 'passbook.core.forms.factors.PasswordFactorForm' | ||||
| @ -94,6 +103,13 @@ class PasswordFactor(Factor): | ||||
|     def has_user_settings(self): | ||||
|         return _('Change Password'), 'pficon-key', 'passbook_core:user-change-password' | ||||
|  | ||||
|     def password_passes(self, user: User) -> bool: | ||||
|         """Return true if user's password passes, otherwise False or raise Exception""" | ||||
|         for policy in self.policies.all(): | ||||
|             if not policy.passes(user): | ||||
|                 return False | ||||
|         return True | ||||
|  | ||||
|     def __str__(self): | ||||
|         return "Password Factor %s" % self.slug | ||||
|  | ||||
| @ -155,10 +171,15 @@ class Source(PolicyModel): | ||||
|         return False | ||||
|  | ||||
|     @property | ||||
|     def get_url(self): | ||||
|         """Return URL used for logging in""" | ||||
|     def get_login_button(self): | ||||
|         """Return a tuple of URL, Icon name and Name""" | ||||
|         raise NotImplementedError | ||||
|  | ||||
|     @property | ||||
|     def additional_info(self): | ||||
|         """Return additional Info, such as a callback URL. Show in the administration interface.""" | ||||
|         return None | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.name | ||||
|  | ||||
|  | ||||
| @ -73,6 +73,7 @@ INSTALLED_APPS = [ | ||||
|     'passbook.saml_idp.apps.PassbookSAMLIDPConfig', | ||||
|     'passbook.otp.apps.PassbookOTPConfig', | ||||
|     'passbook.captcha_factor.apps.PassbookCaptchaFactorConfig', | ||||
|     'passbook.hibp_policy.apps.PassbookHIBPConfig', | ||||
| ] | ||||
|  | ||||
| # Message Tag fix for bootstrap CSS Classes | ||||
|  | ||||
| @ -9,3 +9,4 @@ from django.core.signals import Signal | ||||
| user_signed_up = Signal(providing_args=['request', 'user']) | ||||
| invitation_created = Signal(providing_args=['request', 'invitation']) | ||||
| invitation_used = Signal(providing_args=['request', 'invitation', 'user']) | ||||
| password_changed = Signal(providing_args=['user', 'password']) | ||||
|  | ||||
| @ -23,16 +23,16 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| <div class="toast-notifications-list-pf"> | ||||
|     {% include 'partials/messages.html' %} | ||||
| </div> | ||||
| <div class="login-pf-page"> | ||||
|     <div class="container-fluid"> | ||||
|         <div class="row"> | ||||
|             <div class="col-md-6 col-md-offset-3"> | ||||
|                 {% include 'partials/messages.html' %} | ||||
|             </div> | ||||
|             <div class="col-sm-6 col-sm-offset-3 col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4"> | ||||
|                 <header class="login-pf-page-header"> | ||||
|                     <img class="login-pf-brand" style="max-height: 10rem;" src="{% static 'img/logo.svg' %}" | ||||
|                         alt="PatternFly logo" /> | ||||
|                         alt="passbook logo" /> | ||||
|                     {% if config.login.subtext %} | ||||
|                     <p>{{ config.login.subtext }}</p> | ||||
|                     {% endif %} | ||||
|  | ||||
| @ -2,3 +2,8 @@ | ||||
|  | ||||
| {% load i18n %} | ||||
|  | ||||
| {% block beneath_form %} | ||||
| {% if show_password_forget_notice %} | ||||
| <a href="{% url 'passbook_core:auth-process' %}?password-forgotten">{% trans 'Forgot password?' %}</a> | ||||
| {% endif %} | ||||
| {% endblock %} | ||||
|  | ||||
| @ -8,10 +8,11 @@ | ||||
|   <h1>{% trans title %}</h1> | ||||
| </header> | ||||
| <form method="POST"> | ||||
|   {% csrf_token %} | ||||
|   {% block above_form %} | ||||
|   {% endblock %} | ||||
|   {% include 'partials/form_login.html' %} | ||||
|   {% block beneath_form %} | ||||
|   {% endblock %} | ||||
|   <button type="submit" class="btn btn-primary btn-block btn-lg">{% trans primary_action %}</button> | ||||
| </form> | ||||
| {% if show_sign_up_notice %} | ||||
|  | ||||
| @ -1,131 +0,0 @@ | ||||
| {% load static %} | ||||
| <!DOCTYPE html> | ||||
| <!--[if IE 9]><html lang="en-us" class="ie9 login-pf"><![endif]--> | ||||
| <!--[if gt IE 9]><!--> | ||||
| <html lang="en-us" class="login-pf"> | ||||
| <!--<![endif]--> | ||||
|   <head> | ||||
|     <title>Login Social Account (two column) - Red Hat® Common User Experience</title> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=Edge"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <link rel="shortcut icon" href="{% static 'img/favicon.ico' %}"> | ||||
|     <!-- iPad retina icon --> | ||||
|     <link rel="apple-touch-icon-precomposed" sizes="152x152" href="{% static 'img/apple-touch-icon-precomposed-152.png' %}"> | ||||
|     <!-- iPad retina icon (iOS < 7) --> | ||||
|     <link rel="apple-touch-icon-precomposed" sizes="144x144" href="{% static 'img/apple-touch-icon-precomposed-144.png' %}"> | ||||
|     <!-- iPad non-retina icon --> | ||||
|     <link rel="apple-touch-icon-precomposed" sizes="76x76" href="{% static 'img/apple-touch-icon-precomposed-76.png' %}"> | ||||
|     <!-- iPad non-retina icon (iOS < 7) --> | ||||
|     <link rel="apple-touch-icon-precomposed" sizes="72x72" href="{% static 'img/apple-touch-icon-precomposed-72.png' %}"> | ||||
|     <!-- iPhone 6 Plus icon --> | ||||
|     <link rel="apple-touch-icon-precomposed" sizes="120x120" href="{% static 'img/apple-touch-icon-precomposed-180.png' %}"> | ||||
|     <!-- iPhone retina icon (iOS < 7) --> | ||||
|     <link rel="apple-touch-icon-precomposed" sizes="114x114" href="{% static 'img/apple-touch-icon-precomposed-114.png' %}"> | ||||
|     <!-- iPhone non-retina icon (iOS < 7) --> | ||||
|     <link rel="apple-touch-icon-precomposed" sizes="57x57" href="{% static 'img/apple-touch-icon-precomposed-57.png' %}"> | ||||
|     <link rel="stylesheet" type="text/css" href="{% static 'css/patternfly.min.css' %}"> | ||||
|     <link rel="stylesheet" type="text/css" href="{% static 'css/patternfly-additions.min.css' %}"> | ||||
|  | ||||
|     <script src="{% static 'js/jquery.min.js' %}"></script> | ||||
|     <script src="{% static 'js/bootstrap.min.js' %}"></script> | ||||
|     <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.2/js/bootstrap-select.min.js"></script> | ||||
|     <script src="{% static 'js/patternfly.min.js' %}"></script> | ||||
|   </head> | ||||
|   <div class="toast-notifications-list-pf"> | ||||
|     <div class="toast-pf alert alert-warning alert-dismissable"> | ||||
|       <button type="button" class="close" data-dismiss="alert" aria-hidden="true"> | ||||
|         <span class="pficon pficon-close"></span> | ||||
|       </button> | ||||
|       <span class="pficon pficon-warning-triangle-o"></span> | ||||
|       These examples are included for development testing purposes.  For official documentation, see <a href="https://www.patternfly.org" class="alert-link">https://www.patternfly.org</a> and <a href="http://getbootstrap.com" class="alert-link">http://getbootstrap.com</a>. | ||||
|     </div> | ||||
|   </div> | ||||
|  | ||||
|   <body> | ||||
| <div class="login-pf-page login-pf-page-accounts"> | ||||
|           <header class="login-pf-page-header"> | ||||
|             <img class="login-pf-brand" src="/" alt="Red Hat® logo" /> | ||||
|           </header> | ||||
|           <div class="card-pf login-pf-accounts"> | ||||
|               <header class="login-pf-header"> | ||||
|                 <select class="selectpicker"> | ||||
|                   <option>English</option> | ||||
|                   <option>French</option> | ||||
|                   <option>Italian</option> | ||||
|                 </select> | ||||
|                 <h1>Log In to Your Account</h1> | ||||
|               </header> | ||||
|                     <section class="login-pf-social-section" role="contentinfo" aria-label="Log in to your patternfly account"> | ||||
|                     <form> | ||||
|                       <div class="form-group"> | ||||
|                         <label class="sr-only" for="exampleInputEmail1">Email address</label> | ||||
|                         <input type="email" class="form-control  input-lg" id="exampleInputEmail1" placeholder="Email address"> | ||||
|                       </div> | ||||
|                       <div class="form-group"> | ||||
|                         <label class="sr-only"  for="exampleInputPassword1">Password | ||||
|                         </label> | ||||
|                         <input type="password" class="form-control input-lg" id="exampleInputPassword1" placeholder="Password"> | ||||
|                       </div> | ||||
|                       <div class="login-pf-settings"> | ||||
|                             <label class="checkbox-label"> | ||||
|                               <input type="checkbox"> Keep me logged in for 30 days | ||||
|                             </label> | ||||
|                             <a href="#">Forgot password?</a> | ||||
|                       </div> | ||||
|                       <button type="submit" class="btn btn-primary btn-block btn-lg">Log In</button> | ||||
|                     </form> | ||||
|                 </section><!--login-pf-section--> | ||||
|                 <section class="login-pf-social-section" role="contentinfo" aria-label="Log in with third party account"> | ||||
|                   <ul class="login-pf-social login-pf-social-double-col list-unstyled"> | ||||
|                     <li class="login-pf-social-link"><a href="#"><img src="{% static 'img/google-logo.svg' %}" alt="Google account login">Google</a></li> | ||||
|                     <li class="login-pf-social-link"><a href="#"><img src="{% static 'img/github-logo.svg' %}" alt="github account login">Github</a></li> | ||||
|                     <li class="login-pf-social-link"><a href="#"><img src="{% static 'img/facebook-logo.svg' %}" alt="Facebook account login">Facebook</a></li> | ||||
|                     <li class="login-pf-social-link"><a href="#"><img src="{% static 'img/twitter-logo.svg' %}" alt="Twitter account login">Twitter</a></li> | ||||
|                     <li class="login-pf-social-link"><a href="#"><img src="{% static 'img/linkedin-logo.svg' %}" alt="LinkIn account login">LinkIn</a></li> | ||||
|                     <li class="login-pf-social-link"><a href="#"><img src="{% static 'img/stack-exchange-logo.svg' %}" alt="Stack Exchange logo">Stack Exchange</a></li> | ||||
|                     <li class="login-pf-social-link"><a href="#"><img src="{% static 'img/open-id-logo.svg' %}" alt="Open ID account login">Open ID</a></li> | ||||
|                     <li class="login-pf-social-link"><a href="#"><img src="{% static 'img/instagram-logo.png' %}" alt="Instagram account login">Instagram</a></li> | ||||
|                     <li class="login-pf-social-link login-pf-social-link-more"><a href="#"><img src="{% static 'img/git-logo.svg' %}" alt="Git account login">Git</a></li> | ||||
|                     <li class="login-pf-social-link login-pf-social-link-more"><a href="#"><img src="{% static 'img/dropbox-logo.svg' %}" alt="dropbox account login">Dropbox</a></li> | ||||
|                     <li class="login-pf-social-link login-pf-social-link-more"><a href="#"><img src="{% static 'img/fedora-logo.png' %}" alt="fedora account login">Fedora</a></li> | ||||
|                     <li class="login-pf-social-link login-pf-social-link-more"><a href="#"><img src="{% static 'img/skype-logo.svg' %}" alt="skype account logingit ">Skype</a></li> | ||||
|                   </ul> | ||||
|                   <button type="button" id="socialAccountsToggle" class="btn btn-link login-pf-social-toggle">More<span class="caret"></span></button> | ||||
|               </section><!--login-pf-section--> | ||||
|                 <p class="login-pf-signup">Need an account?<a href="#">Sign up</a></p> | ||||
|         </div><!-- card --> | ||||
|         <div class="row"> | ||||
|           <div class="col-md-6 col-md-offset-3"> | ||||
|             <footer class="login-pf-page-footer"> | ||||
|               <ul class="login-pf-page-footer-links list-unstyled"> | ||||
|                 <li><a class="login-pf-page-footer-link" href="#">Terms of Use</a></li> | ||||
|                 <li><a class="login-pf-page-footer-link" href="#">Help</a></li> | ||||
|                 <li><a class="login-pf-page-footer-link" href="#">Privacy Policy</a></li> | ||||
|               </ul> | ||||
|             </footer> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
| </div><!-- login-pf-page --> | ||||
| <script> | ||||
|   $("#socialAccountsToggle").on("click", function(e) { | ||||
|     var $toggle = $(e.target); | ||||
|     var text = $toggle.contents().first()[0]; | ||||
|     var socialContainer = $('.login-pf-social-section > .login-pf-social'); | ||||
|  | ||||
|     if ($toggle.hasClass('login-pf-social-toggle-active')) { | ||||
|       $toggle.removeClass('login-pf-social-toggle-active'); | ||||
|       text.textContent = 'More'; | ||||
|       socialContainer.removeClass('login-pf-social-all'); | ||||
|     } else { | ||||
|       $toggle.addClass('login-pf-social-toggle-active'); | ||||
|       text.textContent = 'Less'; | ||||
|       socialContainer.addClass('login-pf-social-all'); | ||||
|     } | ||||
|   }); | ||||
| </script> | ||||
|  | ||||
|  | ||||
|   </body> | ||||
| </html> | ||||
| @ -1,45 +1,71 @@ | ||||
| {% extends 'login/base.html' %} | ||||
| {% extends 'base/skeleton.html' %} | ||||
|  | ||||
| {% load static %} | ||||
| {% load i18n %} | ||||
|  | ||||
| {% block row %} | ||||
| {% include 'partials/messages.html' %} | ||||
| <div class="col-md-6"> | ||||
|     <div class="card-pf"> | ||||
| {% block head %} | ||||
| <style> | ||||
|     .login-pf-page .login-pf-page-footer-links { | ||||
|         padding: 15px; | ||||
|         background-color: #fff; | ||||
|         border-top: 2px solid transparent; | ||||
|         box-shadow: 0 1px 1px rgba(3, 3, 3, .175); | ||||
|     } | ||||
|  | ||||
|     .login-pf-page .login-pf-page-footer-link { | ||||
|         color: #72767b; | ||||
|     } | ||||
|  | ||||
|     .login-pf-page .login-pf-page-footer-links li:not(:last-of-type):after { | ||||
|         color: #72767b; | ||||
|     } | ||||
| </style> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| <div class="toast-notifications-list-pf"> | ||||
|     {% include 'partials/messages.html' %} | ||||
| </div> | ||||
| <div class="login-pf-page login-pf-page-accounts"> | ||||
|     <header class="login-pf-page-header"> | ||||
|         <img class="login-pf-brand" style="max-height: 10rem;" src="{% static 'img/logo.svg' %}" alt="passbook logo" /> | ||||
|         {% if config.login.subtext %} | ||||
|         <p>{{ config.login.subtext }}</p> | ||||
|         {% endif %} | ||||
|     </header> | ||||
|     <div class="card-pf login-pf-accounts"> | ||||
|         <header class="login-pf-header"> | ||||
|             <h1>{% trans title %}</h1> | ||||
|         </header> | ||||
|         <section class="login-pf-social-section" role="contentinfo" aria-label="Log in to your patternfly account"> | ||||
|             <form method="POST"> | ||||
|             {% csrf_token %} | ||||
|                 {% block above_form %} | ||||
|                 {% endblock %} | ||||
|                 {% include 'partials/form_login.html' %} | ||||
|                 <button type="submit" class="btn btn-primary btn-block btn-lg">{% trans primary_action %}</button> | ||||
|             </form> | ||||
|         </section> | ||||
|         <!--login-pf-section--> | ||||
|         <section class="login-pf-social-section" role="contentinfo" aria-label="Log in with third party account"> | ||||
|             <ul class="login-pf-social login-pf-social-double-col list-unstyled"> | ||||
|                 {% for url, icon, name in sources %} | ||||
|                 <li class="login-pf-social-link"> | ||||
|                     <a href="{{ url }}"> | ||||
|                         <img src="{% static 'img/' %}{{ icon }}.svg" alt="{{ name }}"> {{ name }} | ||||
|                     </a> | ||||
|                 </li> | ||||
|                 {% endfor %} | ||||
|             </ul> | ||||
|         </section> | ||||
|         {% if show_sign_up_notice %} | ||||
|         <p class="login-pf-signup"> | ||||
|             {% trans 'Need an account?' %} | ||||
|             <a href="{% url 'passbook_core:auth-sign-up' %}">{% trans 'Sign up' %}</a> | ||||
|         </p> | ||||
|         {% endif %} | ||||
|     </div> | ||||
| </div> | ||||
| <div class="col-md-6"> | ||||
|     <div class="card-pf"> | ||||
|         <header class="login-pf-header"> | ||||
|             <h1>{% trans title %}</h1> | ||||
|             <ul> | ||||
|                 {% for source in sources %} | ||||
|                 <li> | ||||
|                     <a class="btn btn-block btn-primary" href="{{ source.get_url }}">{{ source }}</a> | ||||
|                 </li> | ||||
|                 {% endfor %} | ||||
|             </ul> | ||||
|         </header> | ||||
|     </div> | ||||
| </div> | ||||
| <div class="col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-8 col-lg-offset-2"> | ||||
|     </div><!-- card --> | ||||
|     <div class="row"> | ||||
|         <div class="col-md-6 col-md-offset-3"> | ||||
|             <footer class="login-pf-page-footer"> | ||||
|                 <ul class="login-pf-page-footer-links list-unstyled"> | ||||
|                     <li><a class="login-pf-page-footer-link" href="#">Terms of Use</a></li> | ||||
| @ -47,5 +73,7 @@ | ||||
|                     <li><a class="login-pf-page-footer-link" href="#">Privacy Policy</a></li> | ||||
|                 </ul> | ||||
|             </footer> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| {% endblock %} | ||||
|  | ||||
| @ -5,6 +5,9 @@ | ||||
| {% load is_active %} | ||||
|  | ||||
| {% block body %} | ||||
| <div class="toast-notifications-list-pf"> | ||||
|     {% include 'partials/messages.html' %} | ||||
| </div> | ||||
| <nav class="navbar navbar-default navbar-pf" role="navigation"> | ||||
|     <div class="navbar-header"> | ||||
|         <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse-1"> | ||||
| @ -20,8 +23,8 @@ | ||||
|     <div class="collapse navbar-collapse navbar-collapse-1"> | ||||
|         <ul class="nav navbar-nav navbar-utility"> | ||||
|             <li class="dropdown"> | ||||
|         <button class="btn btn-link nav-item-iconic" id="horizontalDropdownMenu1" data-toggle="dropdown" aria-haspopup="true" | ||||
|           aria-expanded="true"> | ||||
|                 <button class="btn btn-link nav-item-iconic" id="horizontalDropdownMenu1" data-toggle="dropdown" | ||||
|                     aria-haspopup="true" aria-expanded="true"> | ||||
|                     <span title="Help" class="fa pficon-help dropdown-title"></span> | ||||
|                 </button> | ||||
|                 <ul class="dropdown-menu" aria-labelledby="horizontalDropdownMenu1"> | ||||
| @ -66,9 +69,6 @@ | ||||
|     </div> | ||||
| </nav> | ||||
| <div class="container-fluid container-cards-pf"> | ||||
|   <div class="container"> | ||||
|     {% include 'partials/messages.html' %} | ||||
|   </div> | ||||
|     {% block content %} | ||||
|     {% endblock %} | ||||
| </div> | ||||
|  | ||||
| @ -25,9 +25,6 @@ | ||||
|     <label class="checkbox-label"> | ||||
|       {{ field }} {{ field.label }} | ||||
|     </label> | ||||
|     {% if show_password_forget_notice %} | ||||
|     <a href="#">{% trans 'Forgot password?' %}</a> | ||||
|     {% endif %} | ||||
|   {% else %} | ||||
|   <label class="col-sm-2 sr-only" {% if field.field.required %}class="required"{% endif %} for="{{ field.name }}-{{ forloop.counter0 }}"> | ||||
|     {{ field.label }} | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| {% if messages %} | ||||
|   {% for msg in messages %} | ||||
|   <div class="alert alert-{{ msg.level_tag }}"> | ||||
| {% for msg in messages %} | ||||
| <div class="toast-pf alert alert-dismissable alert-{{ msg.level_tag }}"> | ||||
|     <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||||
|         <span class="pficon pficon-close"></span> | ||||
|     </button> | ||||
| @ -13,7 +13,7 @@ | ||||
|     {% elif msg.level_tag == 'info' %} | ||||
|     <span class="pficon pficon-info"></span> | ||||
|     {% endif %} | ||||
|     <strong>{{ msg.message|safe }}</strong> | ||||
|   </div> | ||||
|   {% endfor %} | ||||
|     {{ msg.message|safe }} | ||||
| </div> | ||||
| {% endfor %} | ||||
| {% endif %} | ||||
|  | ||||
							
								
								
									
										0
									
								
								passbook/core/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								passbook/core/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										10
									
								
								passbook/core/tests/test_login.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								passbook/core/tests/test_login.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| """passbook core login test""" | ||||
|  | ||||
| from django.test import TestCase | ||||
|  | ||||
|  | ||||
| class LoginTest(TestCase): | ||||
|     """Test login""" | ||||
|  | ||||
|     def test(self): | ||||
|         """Stub test""" | ||||
| @ -41,10 +41,12 @@ class LoginView(UserPassesTestMixin, FormView): | ||||
|         kwargs['title'] = _('Log in to your account') | ||||
|         kwargs['primary_action'] = _('Log in') | ||||
|         kwargs['show_sign_up_notice'] = CONFIG.y('passbook.sign_up.enabled') | ||||
|         kwargs['show_password_forget_notice'] = CONFIG.y('passbook.password_reset.enabled') | ||||
|         kwargs['sources'] = Source.objects.filter(enabled=True).select_subclasses() | ||||
|         if any(source.is_link for source in kwargs['sources']): | ||||
|             self.template_name = 'login/test.html' | ||||
|         kwargs['sources'] = [] | ||||
|         sources = Source.objects.filter(enabled=True).select_subclasses() | ||||
|         if any(source.is_link for source in sources): | ||||
|             for source in sources: | ||||
|                 kwargs['sources'].append(source.get_login_button) | ||||
|             self.template_name = 'login/with_sources.html' | ||||
|         return super().get_context_data(**kwargs) | ||||
|  | ||||
|     def get_user(self, uid_value) -> User: | ||||
|  | ||||
							
								
								
									
										2
									
								
								passbook/hibp_policy/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								passbook/hibp_policy/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| """passbook hibp_policy""" | ||||
| __version__ = '0.0.7-alpha' | ||||
							
								
								
									
										5
									
								
								passbook/hibp_policy/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								passbook/hibp_policy/admin.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| """Passbook HIBP Admin""" | ||||
|  | ||||
| from passbook.lib.admin import admin_autoregister | ||||
|  | ||||
| admin_autoregister('passbook_hibp_policy') | ||||
							
								
								
									
										11
									
								
								passbook/hibp_policy/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								passbook/hibp_policy/apps.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| """Passbook hibp app config""" | ||||
|  | ||||
| from django.apps import AppConfig | ||||
|  | ||||
|  | ||||
| class PassbookHIBPConfig(AppConfig): | ||||
|     """Passbook hibp app config""" | ||||
|  | ||||
|     name = 'passbook.hibp_policy' | ||||
|     label = 'passbook_hibp_policy' | ||||
|     verbose_name = 'passbook HaveIBeenPwned Policy' | ||||
							
								
								
									
										19
									
								
								passbook/hibp_policy/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								passbook/hibp_policy/forms.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| """passbook HaveIBeenPwned Policy forms""" | ||||
|  | ||||
| from django import forms | ||||
|  | ||||
| from passbook.core.forms.policies import GENERAL_FIELDS | ||||
| from passbook.hibp_policy.models import HaveIBeenPwendPolicy | ||||
|  | ||||
|  | ||||
| class HaveIBeenPwnedPolicyForm(forms.ModelForm): | ||||
|     """Edit HaveIBeenPwendPolicy instances""" | ||||
|  | ||||
|     class Meta: | ||||
|  | ||||
|         model = HaveIBeenPwendPolicy | ||||
|         fields = GENERAL_FIELDS + ['allowed_count'] | ||||
|         widgets = { | ||||
|             'name': forms.TextInput(), | ||||
|             'order': forms.NumberInput(), | ||||
|         } | ||||
							
								
								
									
										28
									
								
								passbook/hibp_policy/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								passbook/hibp_policy/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| # Generated by Django 2.1.7 on 2019-02-25 15:50 | ||||
|  | ||||
| import django.db.models.deletion | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     initial = True | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('passbook_core', '0011_auto_20190225_1438'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name='HaveIBeenPwendPolicy', | ||||
|             fields=[ | ||||
|                 ('policy_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.Policy')), | ||||
|                 ('allowed_count', models.IntegerField(default=0)), | ||||
|             ], | ||||
|             options={ | ||||
|                 'verbose_name': 'HaveIBeenPwned Policy', | ||||
|                 'verbose_name_plural': 'HaveIBeenPwned Policies', | ||||
|             }, | ||||
|             bases=('passbook_core.policy',), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										0
									
								
								passbook/hibp_policy/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								passbook/hibp_policy/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										43
									
								
								passbook/hibp_policy/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								passbook/hibp_policy/models.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| """passbook HIBP Models""" | ||||
|  | ||||
| from hashlib import sha1 | ||||
|  | ||||
| from django.db import models | ||||
| from django.utils.translation import gettext as _ | ||||
| from requests import get | ||||
|  | ||||
| from passbook.core.models import Policy, User | ||||
|  | ||||
|  | ||||
| class HaveIBeenPwendPolicy(Policy): | ||||
|     """Check if password is on HaveIBeenPwned's list by upload the first | ||||
|     5 characters of the SHA1 Hash.""" | ||||
|  | ||||
|     allowed_count = models.IntegerField(default=0) | ||||
|  | ||||
|     form = 'passbook.hibp_policy.forms.HaveIBeenPwnedPolicyForm' | ||||
|  | ||||
|     def passes(self, user: User) -> bool: | ||||
|         """Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5 | ||||
|         characters of Password in request and checks if full hash is in response. Returns 0 | ||||
|         if Password is not in result otherwise the count of how many times it was used.""" | ||||
|         # Only check if password is being set | ||||
|         if not hasattr(user, '__password__'): | ||||
|             return True | ||||
|         password = getattr(user, '__password__') | ||||
|         pw_hash = sha1(password.encode('utf-8')).hexdigest() # nosec | ||||
|         url = 'https://api.pwnedpasswords.com/range/%s' % pw_hash[:5] | ||||
|         result = get(url).text | ||||
|         final_count = 0 | ||||
|         for line in result.split('\r\n'): | ||||
|             full_hash, count = line.split(':') | ||||
|             if pw_hash[5:] == full_hash.lower(): | ||||
|                 final_count = int(count) | ||||
|         if final_count > self.allowed_count: | ||||
|             return False | ||||
|         return True | ||||
|  | ||||
|     class Meta: | ||||
|  | ||||
|         verbose_name = _('have i been pwned Policy') | ||||
|         verbose_name_plural = _('have i been pwned Policies') | ||||
| @ -1,2 +1,2 @@ | ||||
| """Passbook ldap app Header""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
| @ -29,7 +29,7 @@ class LDAPSource(Source): | ||||
|     form = 'passbook.ldap.forms.LDAPSourceForm' | ||||
|  | ||||
|     @property | ||||
|     def get_url(self): | ||||
|     def get_login_button(self): | ||||
|         raise NotImplementedError() | ||||
|  | ||||
|     class Meta: | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook lib""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook oauth_client Header""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
| @ -26,8 +26,16 @@ class OAuthSource(Source): | ||||
|         return True | ||||
|  | ||||
|     @property | ||||
|     def get_url(self): | ||||
|         return reverse_lazy('passbook_oauth_client:oauth-client-login', | ||||
|     def get_login_button(self): | ||||
|         url = reverse_lazy('passbook_oauth_client:oauth-client-login', | ||||
|                            kwargs={'source_slug': self.slug}) | ||||
|         if self.provider_type == 'github': | ||||
|             return url, 'github-logo', _('GitHub') | ||||
|         return url, 'generic', _('Generic') | ||||
|  | ||||
|     @property | ||||
|     def additional_info(self): | ||||
|         return "Callback URL: '%s'" % reverse_lazy('passbook_oauth_client:oauth-client-callback', | ||||
|                                                    kwargs={'source_slug': self.slug}) | ||||
|  | ||||
|     class Meta: | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook oauth_provider Header""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook otp Header""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook saml_idp Header""" | ||||
| __version__ = '0.0.7-alpha' | ||||
| __version__ = '0.0.8-alpha' | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	