Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
7437bcfe
Commit
7437bcfe
authored
Jun 21, 2015
by
Braden MacDonald
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New provider config options, New Institution Login Menu - PR 8603
parent
5bf0b179
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
559 additions
and
54 deletions
+559
-54
common/djangoapps/student/views.py
+14
-9
common/djangoapps/third_party_auth/migrations/0003_add_config_options.py
+162
-0
common/djangoapps/third_party_auth/models.py
+27
-3
common/djangoapps/third_party_auth/pipeline.py
+7
-0
common/djangoapps/third_party_auth/tests/specs/test_testshib.py
+8
-2
lms/djangoapps/student_account/test/test_views.py
+1
-0
lms/djangoapps/student_account/views.py
+8
-4
lms/envs/common.py
+1
-0
lms/static/js/spec/main.js
+11
-0
lms/static/js/spec/student_account/access_spec.js
+27
-0
lms/static/js/spec/student_account/institution_login_spec.js
+80
-0
lms/static/js/student_account/views/AccessView.js
+21
-5
lms/static/js/student_account/views/FormView.js
+3
-1
lms/static/js/student_account/views/InstitutionLoginView.js
+30
-0
lms/static/js/student_account/views/LoginView.js
+4
-0
lms/static/js/student_account/views/RegisterView.js
+20
-1
lms/static/sass/views/_login-register.scss
+54
-26
lms/templates/student_account/access.underscore
+4
-0
lms/templates/student_account/institution_login.underscore
+31
-0
lms/templates/student_account/institution_register.underscore
+31
-0
lms/templates/student_account/login.underscore
+7
-1
lms/templates/student_account/login_and_register.html
+1
-1
lms/templates/student_account/register.underscore
+7
-1
No files found.
common/djangoapps/student/views.py
View file @
7437bcfe
...
@@ -1498,6 +1498,13 @@ def create_account_with_params(request, params):
...
@@ -1498,6 +1498,13 @@ def create_account_with_params(request, params):
dog_stats_api
.
increment
(
"common.student.account_created"
)
dog_stats_api
.
increment
(
"common.student.account_created"
)
# If the user is registering via 3rd party auth, track which provider they use
third_party_provider
=
None
running_pipeline
=
None
if
third_party_auth
.
is_enabled
()
and
pipeline
.
running
(
request
):
running_pipeline
=
pipeline
.
get
(
request
)
third_party_provider
=
provider
.
Registry
.
get_from_pipeline
(
running_pipeline
)
# Track the user's registration
# Track the user's registration
if
settings
.
FEATURES
.
get
(
'SEGMENT_IO_LMS'
)
and
hasattr
(
settings
,
'SEGMENT_IO_LMS_KEY'
):
if
settings
.
FEATURES
.
get
(
'SEGMENT_IO_LMS'
)
and
hasattr
(
settings
,
'SEGMENT_IO_LMS_KEY'
):
tracking_context
=
tracker
.
get_tracker
()
.
resolve_context
()
tracking_context
=
tracker
.
get_tracker
()
.
resolve_context
()
...
@@ -1506,20 +1513,13 @@ def create_account_with_params(request, params):
...
@@ -1506,20 +1513,13 @@ def create_account_with_params(request, params):
'username'
:
user
.
username
,
'username'
:
user
.
username
,
})
})
# If the user is registering via 3rd party auth, track which provider they use
provider_name
=
None
if
third_party_auth
.
is_enabled
()
and
pipeline
.
running
(
request
):
running_pipeline
=
pipeline
.
get
(
request
)
current_provider
=
provider
.
Registry
.
get_from_pipeline
(
running_pipeline
)
provider_name
=
current_provider
.
name
analytics
.
track
(
analytics
.
track
(
user
.
id
,
user
.
id
,
"edx.bi.user.account.registered"
,
"edx.bi.user.account.registered"
,
{
{
'category'
:
'conversion'
,
'category'
:
'conversion'
,
'label'
:
params
.
get
(
'course_id'
),
'label'
:
params
.
get
(
'course_id'
),
'provider'
:
provider_nam
e
'provider'
:
third_party_provider
.
name
if
third_party_provider
else
Non
e
},
},
context
=
{
context
=
{
'Google Analytics'
:
{
'Google Analytics'
:
{
...
@@ -1536,6 +1536,7 @@ def create_account_with_params(request, params):
...
@@ -1536,6 +1536,7 @@ def create_account_with_params(request, params):
# 2. Random user generation for other forms of testing.
# 2. Random user generation for other forms of testing.
# 3. External auth bypassing activation.
# 3. External auth bypassing activation.
# 4. Have the platform configured to not require e-mail activation.
# 4. Have the platform configured to not require e-mail activation.
# 5. Registering a new user using a trusted third party provider (with skip_email_verification=True)
#
#
# Note that this feature is only tested as a flag set one way or
# Note that this feature is only tested as a flag set one way or
# the other for *new* systems. we need to be careful about
# the other for *new* systems. we need to be careful about
...
@@ -1544,7 +1545,11 @@ def create_account_with_params(request, params):
...
@@ -1544,7 +1545,11 @@ def create_account_with_params(request, params):
send_email
=
(
send_email
=
(
not
settings
.
FEATURES
.
get
(
'SKIP_EMAIL_VALIDATION'
,
None
)
and
not
settings
.
FEATURES
.
get
(
'SKIP_EMAIL_VALIDATION'
,
None
)
and
not
settings
.
FEATURES
.
get
(
'AUTOMATIC_AUTH_FOR_TESTING'
)
and
not
settings
.
FEATURES
.
get
(
'AUTOMATIC_AUTH_FOR_TESTING'
)
and
not
(
do_external_auth
and
settings
.
FEATURES
.
get
(
'BYPASS_ACTIVATION_EMAIL_FOR_EXTAUTH'
))
not
(
do_external_auth
and
settings
.
FEATURES
.
get
(
'BYPASS_ACTIVATION_EMAIL_FOR_EXTAUTH'
))
and
not
(
third_party_provider
and
third_party_provider
.
skip_email_verification
and
user
.
email
==
running_pipeline
[
'kwargs'
]
.
get
(
'details'
,
{})
.
get
(
'email'
)
)
)
)
if
send_email
:
if
send_email
:
context
=
{
context
=
{
...
...
common/djangoapps/third_party_auth/migrations/0003_add_config_options.py
0 → 100644
View file @
7437bcfe
# -*- coding: utf-8 -*-
from
south.utils
import
datetime_utils
as
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Adding field 'SAMLProviderConfig.secondary'
db
.
add_column
(
'third_party_auth_samlproviderconfig'
,
'secondary'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
),
keep_default
=
False
)
# Adding field 'SAMLProviderConfig.skip_registration_form'
db
.
add_column
(
'third_party_auth_samlproviderconfig'
,
'skip_registration_form'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
),
keep_default
=
False
)
# Adding field 'SAMLProviderConfig.skip_email_verification'
db
.
add_column
(
'third_party_auth_samlproviderconfig'
,
'skip_email_verification'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
),
keep_default
=
False
)
# Adding field 'OAuth2ProviderConfig.secondary'
db
.
add_column
(
'third_party_auth_oauth2providerconfig'
,
'secondary'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
),
keep_default
=
False
)
# Adding field 'OAuth2ProviderConfig.skip_registration_form'
db
.
add_column
(
'third_party_auth_oauth2providerconfig'
,
'skip_registration_form'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
),
keep_default
=
False
)
# Adding field 'OAuth2ProviderConfig.skip_email_verification'
db
.
add_column
(
'third_party_auth_oauth2providerconfig'
,
'skip_email_verification'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
),
keep_default
=
False
)
def
backwards
(
self
,
orm
):
# Deleting field 'SAMLProviderConfig.secondary'
db
.
delete_column
(
'third_party_auth_samlproviderconfig'
,
'secondary'
)
# Deleting field 'SAMLProviderConfig.skip_registration_form'
db
.
delete_column
(
'third_party_auth_samlproviderconfig'
,
'skip_registration_form'
)
# Deleting field 'SAMLProviderConfig.skip_email_verification'
db
.
delete_column
(
'third_party_auth_samlproviderconfig'
,
'skip_email_verification'
)
# Deleting field 'OAuth2ProviderConfig.secondary'
db
.
delete_column
(
'third_party_auth_oauth2providerconfig'
,
'secondary'
)
# Deleting field 'OAuth2ProviderConfig.skip_registration_form'
db
.
delete_column
(
'third_party_auth_oauth2providerconfig'
,
'skip_registration_form'
)
# Deleting field 'OAuth2ProviderConfig.skip_email_verification'
db
.
delete_column
(
'third_party_auth_oauth2providerconfig'
,
'skip_email_verification'
)
models
=
{
'auth.group'
:
{
'Meta'
:
{
'object_name'
:
'Group'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'80'
}),
'permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
})
},
'auth.permission'
:
{
'Meta'
:
{
'ordering'
:
"('content_type__app_label', 'content_type__model', 'codename')"
,
'unique_together'
:
"(('content_type', 'codename'),)"
,
'object_name'
:
'Permission'
},
'codename'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['contenttypes.ContentType']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
})
},
'auth.user'
:
{
'Meta'
:
{
'object_name'
:
'User'
},
'date_joined'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'email'
:
(
'django.db.models.fields.EmailField'
,
[],
{
'max_length'
:
'75'
,
'blank'
:
'True'
}),
'first_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'groups'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'is_staff'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'is_superuser'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'last_login'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'last_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
}),
'user_permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'username'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'30'
})
},
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
},
'third_party_auth.oauth2providerconfig'
:
{
'Meta'
:
{
'object_name'
:
'OAuth2ProviderConfig'
},
'backend_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
,
'db_index'
:
'True'
}),
'change_date'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'blank'
:
'True'
}),
'changed_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
,
'null'
:
'True'
,
'on_delete'
:
'models.PROTECT'
}),
'enabled'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'icon_class'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'fa-sign-in'"
,
'max_length'
:
'50'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'key'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
}),
'other_settings'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'secondary'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'secret'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'skip_email_verification'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'skip_registration_form'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
})
},
'third_party_auth.samlconfiguration'
:
{
'Meta'
:
{
'object_name'
:
'SAMLConfiguration'
},
'change_date'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'blank'
:
'True'
}),
'changed_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
,
'null'
:
'True'
,
'on_delete'
:
'models.PROTECT'
}),
'enabled'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'entity_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'http://saml.example.com'"
,
'max_length'
:
'255'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'org_info_str'
:
(
'django.db.models.fields.TextField'
,
[],
{
'default'
:
'
\'
{"en-US": {"url": "http://www.example.com", "displayname": "Example Inc.", "name": "example"}}
\'
'
}),
'other_config_str'
:
(
'django.db.models.fields.TextField'
,
[],
{
'default'
:
'
\'
{
\\
n"SECURITY_CONFIG": {"metadataCacheDuration": 604800, "signMetadata": false}
\\
n}
\'
'
}),
'private_key'
:
(
'django.db.models.fields.TextField'
,
[],
{}),
'public_key'
:
(
'django.db.models.fields.TextField'
,
[],
{})
},
'third_party_auth.samlproviderconfig'
:
{
'Meta'
:
{
'object_name'
:
'SAMLProviderConfig'
},
'attr_email'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'blank'
:
'True'
}),
'attr_first_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'blank'
:
'True'
}),
'attr_full_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'blank'
:
'True'
}),
'attr_last_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'blank'
:
'True'
}),
'attr_user_permanent_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'blank'
:
'True'
}),
'attr_username'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'blank'
:
'True'
}),
'backend_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'tpa-saml'"
,
'max_length'
:
'50'
}),
'change_date'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'blank'
:
'True'
}),
'changed_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
,
'null'
:
'True'
,
'on_delete'
:
'models.PROTECT'
}),
'enabled'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'entity_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'icon_class'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'fa-sign-in'"
,
'max_length'
:
'50'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'idp_slug'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'max_length'
:
'30'
}),
'metadata_source'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
}),
'other_settings'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'secondary'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'skip_email_verification'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'skip_registration_form'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
})
},
'third_party_auth.samlproviderdata'
:
{
'Meta'
:
{
'ordering'
:
"('-fetched_at',)"
,
'object_name'
:
'SAMLProviderData'
},
'entity_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'expires_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'fetched_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'public_key'
:
(
'django.db.models.fields.TextField'
,
[],
{}),
'sso_url'
:
(
'django.db.models.fields.URLField'
,
[],
{
'max_length'
:
'200'
})
}
}
complete_apps
=
[
'third_party_auth'
]
\ No newline at end of file
common/djangoapps/third_party_auth/models.py
View file @
7437bcfe
...
@@ -8,7 +8,7 @@ from django.conf import settings
...
@@ -8,7 +8,7 @@ from django.conf import settings
from
django.core.exceptions
import
ValidationError
from
django.core.exceptions
import
ValidationError
from
django.db
import
models
from
django.db
import
models
from
django.utils
import
timezone
from
django.utils
import
timezone
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
_lazy
as
_
import
json
import
json
import
logging
import
logging
from
social.backends.base
import
BaseAuth
from
social.backends.base
import
BaseAuth
...
@@ -54,7 +54,7 @@ class AuthNotConfigured(SocialAuthBaseException):
...
@@ -54,7 +54,7 @@ class AuthNotConfigured(SocialAuthBaseException):
self
.
provider_name
=
provider_name
self
.
provider_name
=
provider_name
def
__str__
(
self
):
def
__str__
(
self
):
return
_
(
'Authentication with {} is currently unavailable.'
)
.
format
(
return
_
(
'Authentication with {} is currently unavailable.'
)
.
format
(
# pylint: disable=no-member
self
.
provider_name
self
.
provider_name
)
)
...
@@ -68,10 +68,34 @@ class ProviderConfig(ConfigurationModel):
...
@@ -68,10 +68,34 @@ class ProviderConfig(ConfigurationModel):
help_text
=
(
help_text
=
(
'The Font Awesome (or custom) icon class to use on the login button for this provider. '
'The Font Awesome (or custom) icon class to use on the login button for this provider. '
'Examples: fa-google-plus, fa-facebook, fa-linkedin, fa-sign-in, fa-university'
'Examples: fa-google-plus, fa-facebook, fa-linkedin, fa-sign-in, fa-university'
))
),
)
name
=
models
.
CharField
(
max_length
=
50
,
blank
=
False
,
help_text
=
"Name of this provider (shown to users)"
)
name
=
models
.
CharField
(
max_length
=
50
,
blank
=
False
,
help_text
=
"Name of this provider (shown to users)"
)
secondary
=
models
.
BooleanField
(
default
=
False
,
help_text
=
_
(
'Secondary providers are displayed less prominently, '
'in a separate list of "Institution" login providers.'
),
)
skip_registration_form
=
models
.
BooleanField
(
default
=
False
,
help_text
=
_
(
"If this option is enabled, users will not be asked to confirm their details "
"(name, email, etc.) during the registration process. Only select this option "
"for trusted providers that are known to provide accurate user information."
),
)
skip_email_verification
=
models
.
BooleanField
(
default
=
False
,
help_text
=
_
(
"If this option is selected, users will not be required to confirm their "
"email, and their account will be activated immediately upon registration."
),
)
prefix
=
None
# used for provider_id. Set to a string value in subclass
prefix
=
None
# used for provider_id. Set to a string value in subclass
backend_name
=
None
# Set to a field or fixed value in subclass
backend_name
=
None
# Set to a field or fixed value in subclass
# "enabled" field is inherited from ConfigurationModel
# "enabled" field is inherited from ConfigurationModel
class
Meta
(
object
):
# pylint: disable=missing-docstring
class
Meta
(
object
):
# pylint: disable=missing-docstring
...
...
common/djangoapps/third_party_auth/pipeline.py
View file @
7437bcfe
...
@@ -503,12 +503,19 @@ def ensure_user_information(strategy, auth_entry, backend=None, user=None, socia
...
@@ -503,12 +503,19 @@ def ensure_user_information(strategy, auth_entry, backend=None, user=None, socia
"""Redirects to the registration page."""
"""Redirects to the registration page."""
return
redirect
(
AUTH_DISPATCH_URLS
[
AUTH_ENTRY_REGISTER
])
return
redirect
(
AUTH_DISPATCH_URLS
[
AUTH_ENTRY_REGISTER
])
def
should_force_account_creation
():
""" For some third party providers, we auto-create user accounts """
current_provider
=
provider
.
Registry
.
get_from_pipeline
({
'backend'
:
backend
.
name
,
'kwargs'
:
kwargs
})
return
current_provider
and
current_provider
.
skip_email_verification
if
not
user
:
if
not
user
:
if
auth_entry
in
[
AUTH_ENTRY_LOGIN_API
,
AUTH_ENTRY_REGISTER_API
]:
if
auth_entry
in
[
AUTH_ENTRY_LOGIN_API
,
AUTH_ENTRY_REGISTER_API
]:
return
HttpResponseBadRequest
()
return
HttpResponseBadRequest
()
elif
auth_entry
in
[
AUTH_ENTRY_LOGIN
,
AUTH_ENTRY_LOGIN_2
]:
elif
auth_entry
in
[
AUTH_ENTRY_LOGIN
,
AUTH_ENTRY_LOGIN_2
]:
# User has authenticated with the third party provider but we don't know which edX
# User has authenticated with the third party provider but we don't know which edX
# account corresponds to them yet, if any.
# account corresponds to them yet, if any.
if
should_force_account_creation
():
return
dispatch_to_register
()
return
dispatch_to_login
()
return
dispatch_to_login
()
elif
auth_entry
in
[
AUTH_ENTRY_REGISTER
,
AUTH_ENTRY_REGISTER_2
]:
elif
auth_entry
in
[
AUTH_ENTRY_REGISTER
,
AUTH_ENTRY_REGISTER_2
]:
# User has authenticated with the third party provider and now wants to finish
# User has authenticated with the third party provider and now wants to finish
...
...
common/djangoapps/third_party_auth/tests/specs/test_testshib.py
View file @
7437bcfe
"""
"""
Third_party_auth integration tests using a mock version of the TestShib provider
Third_party_auth integration tests using a mock version of the TestShib provider
"""
"""
from
django.contrib.auth.models
import
User
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
import
httpretty
import
httpretty
from
mock
import
patch
from
mock
import
patch
...
@@ -62,8 +63,6 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
...
@@ -62,8 +63,6 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertIn
(
'Authentication with TestShib is currently unavailable.'
,
response
.
content
)
self
.
assertIn
(
'Authentication with TestShib is currently unavailable.'
,
response
.
content
)
# Note: the following patch is only needed until https://github.com/edx/edx-platform/pull/8262 is merged
@patch.dict
(
"django.conf.settings.FEATURES"
,
{
"AUTOMATIC_AUTH_FOR_TESTING"
:
True
})
def
test_register
(
self
):
def
test_register
(
self
):
self
.
_configure_testshib_provider
()
self
.
_configure_testshib_provider
()
self
.
_freeze_time
(
timestamp
=
1434326820
)
# This is the time when the saved request/response was recorded.
self
.
_freeze_time
(
timestamp
=
1434326820
)
# This is the time when the saved request/response was recorded.
...
@@ -107,6 +106,7 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
...
@@ -107,6 +106,7 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
# Now check that we can login again:
# Now check that we can login again:
self
.
client
.
logout
()
self
.
client
.
logout
()
self
.
_verify_user_email
(
'myself@testshib.org'
)
self
.
_test_return_login
()
self
.
_test_return_login
()
def
test_login
(
self
):
def
test_login
(
self
):
...
@@ -222,3 +222,9 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
...
@@ -222,3 +222,9 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
content_type
=
'application/x-www-form-urlencoded'
,
content_type
=
'application/x-www-form-urlencoded'
,
data
=
self
.
_read_data_file
(
'testshib_response.txt'
),
data
=
self
.
_read_data_file
(
'testshib_response.txt'
),
)
)
def
_verify_user_email
(
self
,
email
):
""" Mark the user with the given email as verified """
user
=
User
.
objects
.
get
(
email
=
email
)
user
.
is_active
=
True
user
.
save
()
lms/djangoapps/student_account/test/test_views.py
View file @
7437bcfe
...
@@ -359,6 +359,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
...
@@ -359,6 +359,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
json
.
dumps
({
json
.
dumps
({
"currentProvider"
:
current_provider
,
"currentProvider"
:
current_provider
,
"providers"
:
providers
,
"providers"
:
providers
,
"secondaryProviders"
:
[],
"finishAuthUrl"
:
finish_auth_url
,
"finishAuthUrl"
:
finish_auth_url
,
"errorMessage"
:
None
,
"errorMessage"
:
None
,
})
})
...
...
lms/djangoapps/student_account/views.py
View file @
7437bcfe
...
@@ -164,13 +164,14 @@ def _third_party_auth_context(request, redirect_to):
...
@@ -164,13 +164,14 @@ def _third_party_auth_context(request, redirect_to):
context
=
{
context
=
{
"currentProvider"
:
None
,
"currentProvider"
:
None
,
"providers"
:
[],
"providers"
:
[],
"secondaryProviders"
:
[],
"finishAuthUrl"
:
None
,
"finishAuthUrl"
:
None
,
"errorMessage"
:
None
,
"errorMessage"
:
None
,
}
}
if
third_party_auth
.
is_enabled
():
if
third_party_auth
.
is_enabled
():
context
[
"providers"
]
=
[
for
enabled
in
third_party_auth
.
provider
.
Registry
.
enabled
():
{
info
=
{
"id"
:
enabled
.
provider_id
,
"id"
:
enabled
.
provider_id
,
"name"
:
enabled
.
name
,
"name"
:
enabled
.
name
,
"iconClass"
:
enabled
.
icon_class
,
"iconClass"
:
enabled
.
icon_class
,
...
@@ -185,8 +186,7 @@ def _third_party_auth_context(request, redirect_to):
...
@@ -185,8 +186,7 @@ def _third_party_auth_context(request, redirect_to):
redirect_url
=
redirect_to
,
redirect_url
=
redirect_to
,
),
),
}
}
for
enabled
in
third_party_auth
.
provider
.
Registry
.
enabled
()
context
[
"providers"
if
not
enabled
.
secondary
else
"secondaryProviders"
]
.
append
(
info
)
]
running_pipeline
=
pipeline
.
get
(
request
)
running_pipeline
=
pipeline
.
get
(
request
)
if
running_pipeline
is
not
None
:
if
running_pipeline
is
not
None
:
...
@@ -194,6 +194,10 @@ def _third_party_auth_context(request, redirect_to):
...
@@ -194,6 +194,10 @@ def _third_party_auth_context(request, redirect_to):
context
[
"currentProvider"
]
=
current_provider
.
name
context
[
"currentProvider"
]
=
current_provider
.
name
context
[
"finishAuthUrl"
]
=
pipeline
.
get_complete_url
(
current_provider
.
backend_name
)
context
[
"finishAuthUrl"
]
=
pipeline
.
get_complete_url
(
current_provider
.
backend_name
)
if
current_provider
.
skip_registration_form
:
# As a reliable way of "skipping" the registration form, we just submit it automatically
context
[
"autoSubmitRegForm"
]
=
True
# Check for any error messages we may want to display:
# Check for any error messages we may want to display:
for
msg
in
messages
.
get_messages
(
request
):
for
msg
in
messages
.
get_messages
(
request
):
if
msg
.
extra_tags
.
split
()[
0
]
==
"social-auth"
:
if
msg
.
extra_tags
.
split
()[
0
]
==
"social-auth"
:
...
...
lms/envs/common.py
View file @
7437bcfe
...
@@ -1274,6 +1274,7 @@ student_account_js = [
...
@@ -1274,6 +1274,7 @@ student_account_js = [
'js/student_account/views/RegisterView.js'
,
'js/student_account/views/RegisterView.js'
,
'js/student_account/views/PasswordResetView.js'
,
'js/student_account/views/PasswordResetView.js'
,
'js/student_account/views/AccessView.js'
,
'js/student_account/views/AccessView.js'
,
'js/student_account/views/InstitutionLoginView.js'
,
'js/student_account/accessApp.js'
,
'js/student_account/accessApp.js'
,
]
]
...
...
lms/static/js/spec/main.js
View file @
7437bcfe
...
@@ -84,6 +84,7 @@
...
@@ -84,6 +84,7 @@
'js/student_account/views/FormView'
:
'js/student_account/views/FormView'
,
'js/student_account/views/FormView'
:
'js/student_account/views/FormView'
,
'js/student_account/models/LoginModel'
:
'js/student_account/models/LoginModel'
,
'js/student_account/models/LoginModel'
:
'js/student_account/models/LoginModel'
,
'js/student_account/views/LoginView'
:
'js/student_account/views/LoginView'
,
'js/student_account/views/LoginView'
:
'js/student_account/views/LoginView'
,
'js/student_account/views/InstitutionLoginView'
:
'js/student_account/views/InstitutionLoginView'
,
'js/student_account/models/PasswordResetModel'
:
'js/student_account/models/PasswordResetModel'
,
'js/student_account/models/PasswordResetModel'
:
'js/student_account/models/PasswordResetModel'
,
'js/student_account/views/PasswordResetView'
:
'js/student_account/views/PasswordResetView'
,
'js/student_account/views/PasswordResetView'
:
'js/student_account/views/PasswordResetView'
,
'js/student_account/models/RegisterModel'
:
'js/student_account/models/RegisterModel'
,
'js/student_account/models/RegisterModel'
:
'js/student_account/models/RegisterModel'
,
...
@@ -410,6 +411,14 @@
...
@@ -410,6 +411,14 @@
'js/student_account/views/FormView'
'js/student_account/views/FormView'
]
]
},
},
'js/student_account/views/InstitutionLoginView'
:
{
exports
:
'edx.student.account.InstitutionLoginView'
,
deps
:
[
'jquery'
,
'underscore'
,
'backbone'
]
},
'js/student_account/models/PasswordResetModel'
:
{
'js/student_account/models/PasswordResetModel'
:
{
exports
:
'edx.student.account.PasswordResetModel'
,
exports
:
'edx.student.account.PasswordResetModel'
,
deps
:
[
'jquery'
,
'jquery.cookie'
,
'backbone'
]
deps
:
[
'jquery'
,
'jquery.cookie'
,
'backbone'
]
...
@@ -450,6 +459,7 @@
...
@@ -450,6 +459,7 @@
'js/student_account/views/LoginView'
,
'js/student_account/views/LoginView'
,
'js/student_account/views/PasswordResetView'
,
'js/student_account/views/PasswordResetView'
,
'js/student_account/views/RegisterView'
,
'js/student_account/views/RegisterView'
,
'js/student_account/views/InstitutionLoginView'
,
'js/student_account/models/LoginModel'
,
'js/student_account/models/LoginModel'
,
'js/student_account/models/PasswordResetModel'
,
'js/student_account/models/PasswordResetModel'
,
'js/student_account/models/RegisterModel'
,
'js/student_account/models/RegisterModel'
,
...
@@ -613,6 +623,7 @@
...
@@ -613,6 +623,7 @@
'lms/include/js/spec/student_account/access_spec.js'
,
'lms/include/js/spec/student_account/access_spec.js'
,
'lms/include/js/spec/student_account/finish_auth_spec.js'
,
'lms/include/js/spec/student_account/finish_auth_spec.js'
,
'lms/include/js/spec/student_account/login_spec.js'
,
'lms/include/js/spec/student_account/login_spec.js'
,
'lms/include/js/spec/student_account/institution_login_spec.js'
,
'lms/include/js/spec/student_account/register_spec.js'
,
'lms/include/js/spec/student_account/register_spec.js'
,
'lms/include/js/spec/student_account/password_reset_spec.js'
,
'lms/include/js/spec/student_account/password_reset_spec.js'
,
'lms/include/js/spec/student_account/enrollment_spec.js'
,
'lms/include/js/spec/student_account/enrollment_spec.js'
,
...
...
lms/static/js/spec/student_account/access_spec.js
View file @
7437bcfe
...
@@ -58,6 +58,7 @@ define([
...
@@ -58,6 +58,7 @@ define([
thirdPartyAuth
:
{
thirdPartyAuth
:
{
currentProvider
:
null
,
currentProvider
:
null
,
providers
:
[],
providers
:
[],
secondaryProviders
:
[{
name
:
"provider"
}],
finishAuthUrl
:
finishAuthUrl
finishAuthUrl
:
finishAuthUrl
},
},
nextUrl
:
nextUrl
,
// undefined for default
nextUrl
:
nextUrl
,
// undefined for default
...
@@ -97,6 +98,8 @@ define([
...
@@ -97,6 +98,8 @@ define([
TemplateHelpers
.
installTemplate
(
'templates/student_account/register'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/register'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/password_reset'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/password_reset'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/form_field'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/form_field'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/institution_login'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/institution_register'
);
// Stub analytics tracking
// Stub analytics tracking
window
.
analytics
=
jasmine
.
createSpyObj
(
'analytics'
,
[
'track'
,
'page'
,
'pageview'
,
'trackLink'
]);
window
.
analytics
=
jasmine
.
createSpyObj
(
'analytics'
,
[
'track'
,
'page'
,
'pageview'
,
'trackLink'
]);
...
@@ -135,6 +138,30 @@ define([
...
@@ -135,6 +138,30 @@ define([
assertForms
(
'#login-form'
,
'#register-form'
);
assertForms
(
'#login-form'
,
'#register-form'
);
});
});
it
(
'toggles between the login and institution login view'
,
function
()
{
ajaxSpyAndInitialize
(
this
,
'login'
);
// Simulate clicking on institution login button
$
(
'#login-form .button-secondary-login[data-type="institution_login"]'
).
click
();
assertForms
(
'#institution_login-form'
,
'#login-form'
);
// Simulate selection of the login form
selectForm
(
'login'
);
assertForms
(
'#login-form'
,
'#institution_login-form'
);
});
it
(
'toggles between the register and institution register view'
,
function
()
{
ajaxSpyAndInitialize
(
this
,
'register'
);
// Simulate clicking on institution login button
$
(
'#register-form .button-secondary-login[data-type="institution_login"]'
).
click
();
assertForms
(
'#institution_login-form'
,
'#register-form'
);
// Simulate selection of the login form
selectForm
(
'register'
);
assertForms
(
'#register-form'
,
'#institution_login-form'
);
});
it
(
'displays the reset password form'
,
function
()
{
it
(
'displays the reset password form'
,
function
()
{
ajaxSpyAndInitialize
(
this
,
'login'
);
ajaxSpyAndInitialize
(
this
,
'login'
);
...
...
lms/static/js/spec/student_account/institution_login_spec.js
0 → 100644
View file @
7437bcfe
define
([
'jquery'
,
'underscore'
,
'common/js/spec_helpers/template_helpers'
,
'js/student_account/views/InstitutionLoginView'
,
],
function
(
$
,
_
,
TemplateHelpers
,
InstitutionLoginView
)
{
'use strict'
;
describe
(
'edx.student.account.InstitutionLoginView'
,
function
()
{
var
view
=
null
,
PLATFORM_NAME
=
'edX'
,
THIRD_PARTY_AUTH
=
{
currentProvider
:
null
,
providers
:
[],
secondaryProviders
:
[
{
id
:
'oa2-google-oauth2'
,
name
:
'Google'
,
iconClass
:
'fa-google-plus'
,
loginUrl
:
'/auth/login/google-oauth2/?auth_entry=account_login'
,
registerUrl
:
'/auth/login/google-oauth2/?auth_entry=account_register'
},
{
id
:
'oa2-facebook'
,
name
:
'Facebook'
,
iconClass
:
'fa-facebook'
,
loginUrl
:
'/auth/login/facebook/?auth_entry=account_login'
,
registerUrl
:
'/auth/login/facebook/?auth_entry=account_register'
}
]
};
var
createInstLoginView
=
function
(
mode
)
{
// Initialize the login view
view
=
new
InstitutionLoginView
({
mode
:
mode
,
thirdPartyAuth
:
THIRD_PARTY_AUTH
,
platformName
:
PLATFORM_NAME
});
view
.
render
();
};
beforeEach
(
function
()
{
setFixtures
(
'<div id="institution_login-form"></div>'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/institution_login'
);
TemplateHelpers
.
installTemplate
(
'templates/student_account/institution_register'
);
});
it
(
'displays a list of providers'
,
function
()
{
createInstLoginView
(
'login'
);
expect
(
$
(
'#institution_login-form'
).
html
()).
not
.
toBe
(
""
);
var
$google
=
$
(
'li a:contains("Google")'
);
expect
(
$google
).
toBeVisible
();
expect
(
$google
).
toHaveAttr
(
'href'
,
'/auth/login/google-oauth2/?auth_entry=account_login'
);
var
$facebook
=
$
(
'li a:contains("Facebook")'
);
expect
(
$facebook
).
toBeVisible
();
expect
(
$facebook
).
toHaveAttr
(
'href'
,
'/auth/login/facebook/?auth_entry=account_login'
);
});
it
(
'displays a list of providers'
,
function
()
{
createInstLoginView
(
'register'
);
expect
(
$
(
'#institution_login-form'
).
html
()).
not
.
toBe
(
""
);
var
$google
=
$
(
'li a:contains("Google")'
);
expect
(
$google
).
toBeVisible
();
expect
(
$google
).
toHaveAttr
(
'href'
,
'/auth/login/google-oauth2/?auth_entry=account_register'
);
var
$facebook
=
$
(
'li a:contains("Facebook")'
);
expect
(
$facebook
).
toBeVisible
();
expect
(
$facebook
).
toHaveAttr
(
'href'
,
'/auth/login/facebook/?auth_entry=account_register'
);
});
});
});
lms/static/js/student_account/views/AccessView.js
View file @
7437bcfe
...
@@ -18,7 +18,8 @@ var edx = edx || {};
...
@@ -18,7 +18,8 @@ var edx = edx || {};
subview
:
{
subview
:
{
login
:
{},
login
:
{},
register
:
{},
register
:
{},
passwordHelp
:
{}
passwordHelp
:
{},
institutionLogin
:
{}
},
},
nextUrl
:
'/dashboard'
,
nextUrl
:
'/dashboard'
,
...
@@ -52,7 +53,8 @@ var edx = edx || {};
...
@@ -52,7 +53,8 @@ var edx = edx || {};
this
.
formDescriptions
=
{
this
.
formDescriptions
=
{
login
:
obj
.
loginFormDesc
,
login
:
obj
.
loginFormDesc
,
register
:
obj
.
registrationFormDesc
,
register
:
obj
.
registrationFormDesc
,
reset
:
obj
.
passwordResetFormDesc
reset
:
obj
.
passwordResetFormDesc
,
institution_login
:
null
};
};
this
.
platformName
=
obj
.
platformName
;
this
.
platformName
=
obj
.
platformName
;
...
@@ -148,6 +150,16 @@ var edx = edx || {};
...
@@ -148,6 +150,16 @@ var edx = edx || {};
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
this
.
listenTo
(
this
.
subview
.
register
,
'auth-complete'
,
this
.
authComplete
);
this
.
listenTo
(
this
.
subview
.
register
,
'auth-complete'
,
this
.
authComplete
);
},
institution_login
:
function
(
unused
)
{
this
.
subview
.
institutionLogin
=
new
edx
.
student
.
account
.
InstitutionLoginView
({
thirdPartyAuth
:
this
.
thirdPartyAuth
,
platformName
:
this
.
platformName
,
mode
:
this
.
activeForm
});
this
.
subview
.
institutionLogin
.
render
();
}
}
},
},
...
@@ -180,9 +192,11 @@ var edx = edx || {};
...
@@ -180,9 +192,11 @@ var edx = edx || {};
category
:
'user-engagement'
category
:
'user-engagement'
});
});
if
(
!
this
.
form
.
isLoaded
(
$form
)
)
{
// Load the form. Institution login is always refreshed since it changes based on the previous form.
if
(
!
this
.
form
.
isLoaded
(
$form
)
||
type
==
"institution_login"
)
{
this
.
loadForm
(
type
);
this
.
loadForm
(
type
);
}
}
this
.
activeForm
=
type
;
this
.
element
.
hide
(
$
(
this
.
el
).
find
(
'.submission-success'
)
);
this
.
element
.
hide
(
$
(
this
.
el
).
find
(
'.submission-success'
)
);
this
.
element
.
hide
(
$
(
this
.
el
).
find
(
'.form-wrapper'
)
);
this
.
element
.
hide
(
$
(
this
.
el
).
find
(
'.form-wrapper'
)
);
...
@@ -190,11 +204,13 @@ var edx = edx || {};
...
@@ -190,11 +204,13 @@ var edx = edx || {};
this
.
element
.
scrollTop
(
$anchor
);
this
.
element
.
scrollTop
(
$anchor
);
// Update url without reloading page
// Update url without reloading page
History
.
pushState
(
null
,
document
.
title
,
'/'
+
type
+
queryStr
);
if
(
type
!=
"institution_login"
)
{
History
.
pushState
(
null
,
document
.
title
,
'/'
+
type
+
queryStr
);
}
analytics
.
page
(
'login_and_registration'
,
type
);
analytics
.
page
(
'login_and_registration'
,
type
);
// Focus on the form
// Focus on the form
document
.
getElementById
(
type
).
focus
();
$
(
"#"
+
type
).
focus
();
},
},
/**
/**
...
...
lms/static/js/student_account/views/FormView.js
View file @
7437bcfe
...
@@ -215,7 +215,9 @@ var edx = edx || {};
...
@@ -215,7 +215,9 @@ var edx = edx || {};
submitForm
:
function
(
event
)
{
submitForm
:
function
(
event
)
{
var
data
=
this
.
getFormData
();
var
data
=
this
.
getFormData
();
event
.
preventDefault
();
if
(
!
_
.
isUndefined
(
event
))
{
event
.
preventDefault
();
}
this
.
toggleDisableButton
(
true
);
this
.
toggleDisableButton
(
true
);
...
...
lms/static/js/student_account/views/InstitutionLoginView.js
0 → 100644
View file @
7437bcfe
var
edx
=
edx
||
{};
(
function
(
$
,
_
,
Backbone
)
{
'use strict'
;
edx
.
student
=
edx
.
student
||
{};
edx
.
student
.
account
=
edx
.
student
.
account
||
{};
edx
.
student
.
account
.
InstitutionLoginView
=
Backbone
.
View
.
extend
({
el
:
'#institution_login-form'
,
initialize
:
function
(
data
)
{
var
tpl
=
data
.
mode
==
"register"
?
'#institution_register-tpl'
:
'#institution_login-tpl'
;
this
.
tpl
=
$
(
tpl
).
html
();
this
.
providers
=
data
.
thirdPartyAuth
.
secondaryProviders
||
[];
this
.
platformName
=
data
.
platformName
;
},
render
:
function
()
{
$
(
this
.
el
).
html
(
_
.
template
(
this
.
tpl
,
{
// We pass the context object to the template so that
// we can perform variable interpolation using sprintf
providers
:
this
.
providers
,
platformName
:
this
.
platformName
}));
return
this
;
}
});
})(
jQuery
,
_
,
Backbone
);
lms/static/js/student_account/views/LoginView.js
View file @
7437bcfe
...
@@ -25,6 +25,9 @@ var edx = edx || {};
...
@@ -25,6 +25,9 @@ var edx = edx || {};
preRender
:
function
(
data
)
{
preRender
:
function
(
data
)
{
this
.
providers
=
data
.
thirdPartyAuth
.
providers
||
[];
this
.
providers
=
data
.
thirdPartyAuth
.
providers
||
[];
this
.
hasSecondaryProviders
=
(
data
.
thirdPartyAuth
.
secondaryProviders
&&
data
.
thirdPartyAuth
.
secondaryProviders
.
length
);
this
.
currentProvider
=
data
.
thirdPartyAuth
.
currentProvider
||
''
;
this
.
currentProvider
=
data
.
thirdPartyAuth
.
currentProvider
||
''
;
this
.
errorMessage
=
data
.
thirdPartyAuth
.
errorMessage
||
''
;
this
.
errorMessage
=
data
.
thirdPartyAuth
.
errorMessage
||
''
;
this
.
platformName
=
data
.
platformName
;
this
.
platformName
=
data
.
platformName
;
...
@@ -45,6 +48,7 @@ var edx = edx || {};
...
@@ -45,6 +48,7 @@ var edx = edx || {};
currentProvider
:
this
.
currentProvider
,
currentProvider
:
this
.
currentProvider
,
errorMessage
:
this
.
errorMessage
,
errorMessage
:
this
.
errorMessage
,
providers
:
this
.
providers
,
providers
:
this
.
providers
,
hasSecondaryProviders
:
this
.
hasSecondaryProviders
,
platformName
:
this
.
platformName
platformName
:
this
.
platformName
}
}
}));
}));
...
...
lms/static/js/student_account/views/RegisterView.js
View file @
7437bcfe
...
@@ -22,9 +22,13 @@ var edx = edx || {};
...
@@ -22,9 +22,13 @@ var edx = edx || {};
preRender
:
function
(
data
)
{
preRender
:
function
(
data
)
{
this
.
providers
=
data
.
thirdPartyAuth
.
providers
||
[];
this
.
providers
=
data
.
thirdPartyAuth
.
providers
||
[];
this
.
hasSecondaryProviders
=
(
data
.
thirdPartyAuth
.
secondaryProviders
&&
data
.
thirdPartyAuth
.
secondaryProviders
.
length
);
this
.
currentProvider
=
data
.
thirdPartyAuth
.
currentProvider
||
''
;
this
.
currentProvider
=
data
.
thirdPartyAuth
.
currentProvider
||
''
;
this
.
errorMessage
=
data
.
thirdPartyAuth
.
errorMessage
||
''
;
this
.
errorMessage
=
data
.
thirdPartyAuth
.
errorMessage
||
''
;
this
.
platformName
=
data
.
platformName
;
this
.
platformName
=
data
.
platformName
;
this
.
autoSubmit
=
data
.
thirdPartyAuth
.
autoSubmitRegForm
;
this
.
listenTo
(
this
.
model
,
'sync'
,
this
.
saveSuccess
);
this
.
listenTo
(
this
.
model
,
'sync'
,
this
.
saveSuccess
);
},
},
...
@@ -41,12 +45,19 @@ var edx = edx || {};
...
@@ -41,12 +45,19 @@ var edx = edx || {};
currentProvider
:
this
.
currentProvider
,
currentProvider
:
this
.
currentProvider
,
errorMessage
:
this
.
errorMessage
,
errorMessage
:
this
.
errorMessage
,
providers
:
this
.
providers
,
providers
:
this
.
providers
,
hasSecondaryProviders
:
this
.
hasSecondaryProviders
,
platformName
:
this
.
platformName
platformName
:
this
.
platformName
}
}
}));
}));
this
.
postRender
();
this
.
postRender
();
if
(
this
.
autoSubmit
)
{
$
(
this
.
el
).
hide
();
$
(
'#register-honor_code'
).
prop
(
'checked'
,
true
);
this
.
submitForm
();
}
return
this
;
return
this
;
},
},
...
@@ -63,6 +74,7 @@ var edx = edx || {};
...
@@ -63,6 +74,7 @@ var edx = edx || {};
},
},
saveError
:
function
(
error
)
{
saveError
:
function
(
error
)
{
$
(
this
.
el
).
show
();
// Show in case the form was hidden for auto-submission
this
.
errors
=
_
.
flatten
(
this
.
errors
=
_
.
flatten
(
_
.
map
(
_
.
map
(
JSON
.
parse
(
error
.
responseText
),
JSON
.
parse
(
error
.
responseText
),
...
@@ -76,6 +88,13 @@ var edx = edx || {};
...
@@ -76,6 +88,13 @@ var edx = edx || {};
);
);
this
.
setErrors
();
this
.
setErrors
();
this
.
toggleDisableButton
(
false
);
this
.
toggleDisableButton
(
false
);
}
},
postFormSubmission
:
function
()
{
if
(
_
.
compact
(
this
.
errors
).
length
)
{
// The form did not get submitted due to validation errors.
$
(
this
.
el
).
show
();
// Show in case the form was hidden for auto-submission
}
},
});
});
})(
jQuery
,
_
,
gettext
);
})(
jQuery
,
_
,
gettext
);
lms/static/sass/views/_login-register.scss
View file @
7437bcfe
...
@@ -14,6 +14,7 @@ $sm-btn-linkedin: #0077b5;
...
@@ -14,6 +14,7 @@ $sm-btn-linkedin: #0077b5;
background
:
$white
;
background
:
$white
;
min-height
:
100%
;
min-height
:
100%
;
width
:
100%
;
width
:
100%
;
$third-party-button-height
:
(
$baseline
*
1
.75
);
h2
{
h2
{
@extend
%t-title5
;
@extend
%t-title5
;
...
@@ -22,6 +23,10 @@ $sm-btn-linkedin: #0077b5;
...
@@ -22,6 +23,10 @@ $sm-btn-linkedin: #0077b5;
font-family
:
$sans-serif
;
font-family
:
$sans-serif
;
}
}
.instructions
{
@extend
%t-copy-base
;
}
/* Temp. fix until applied globally */
/* Temp. fix until applied globally */
>
{
>
{
@include
box-sizing
(
border-box
);
@include
box-sizing
(
border-box
);
...
@@ -67,10 +72,11 @@ $sm-btn-linkedin: #0077b5;
...
@@ -67,10 +72,11 @@ $sm-btn-linkedin: #0077b5;
}
}
}
}
form
{
form
,
.wrapper-other-login
{
border
:
1px
solid
$gray-l4
;
border
:
1px
solid
$gray-l4
;
border-radius
:
5px
;
border-radius
:
(
$baseline
/
4
)
;
padding
:
0
px
25px
20px
25px
;
padding
:
0
(
$baseline
*
1
.25
)
$baseline
(
$baseline
*
1
.25
)
;
}
}
.section-title
{
.section-title
{
...
@@ -106,16 +112,20 @@ $sm-btn-linkedin: #0077b5;
...
@@ -106,16 +112,20 @@ $sm-btn-linkedin: #0077b5;
}
}
}
}
.nav-btn
{
%nav-btn-base
{
@extend
%btn-secondary-blue-outline
;
@extend
%btn-secondary-blue-outline
;
width
:
100%
;
width
:
100%
;
height
:
(
$baseline
*
2
);
height
:
(
$baseline
*
2
);
text-transform
:
none
;
text-transform
:
none
;
text-shadow
:
none
;
text-shadow
:
none
;
font-weight
:
600
;
letter-spacing
:
normal
;
letter-spacing
:
normal
;
}
}
.nav-btn
{
@extend
%nav-btn-base
;
@extend
%t-strong
;
}
.form-type
,
.form-type
,
.toggle-form
{
.toggle-form
{
@include
box-sizing
(
border-box
);
@include
box-sizing
(
border-box
);
...
@@ -348,29 +358,31 @@ $sm-btn-linkedin: #0077b5;
...
@@ -348,29 +358,31 @@ $sm-btn-linkedin: #0077b5;
.login-provider {
.login-provider {
@extend %btn-secondary-grey-outline;
@extend %btn-secondary-grey-outline;
width: 130px;
@extend %t-action4;
padding: 0 0 0 ($baseline*2);
height: 34px;
@include padding(0, 0, 0, $baseline*2);
text-align: left;
@include text-align(left);
text-shadow: none;
text-transform: none;
position: relative;
position: relative;
font-size: 0.8em;
margin-right: ($baseline/4);
margin-bottom: $baseline;
border-color: $lightGrey1;
border-color: $lightGrey1;
width: $baseline*6.5;
&:nth-of-type(odd) {
height: $third-party-button-height;
margin-right: 13px
;
text-shadow: none
;
}
text-transform: none;
.icon {
.icon {
color: white;
@include left(0);
position: absolute;
position: absolute;
top: -1px;
top: -1px;
left: 0;
width: 30px;
width: 30px;
height: 34px;
bottom: -1px;
line-height: 34px;
background: $m-blue-d3;
line-height: $third-party-button-height;
text-align: center;
text-align: center;
color: $white;
}
}
&:hover,
&:hover,
...
@@ -378,16 +390,12 @@ $sm-btn-linkedin: #0077b5;
...
@@ -378,16 +390,12 @@ $sm-btn-linkedin: #0077b5;
background-image: none;
background-image: none;
.icon {
.icon {
height: 32px;
line-height: 32px;
top: 0;
top: 0;
bottom: 0;
line-height: ($third-party-button-height - 2px);
}
}
}
}
&:last-child {
margin-bottom: $baseline;
}
&.button-oa2-google-oauth2 {
&.button-oa2-google-oauth2 {
color: $sm-btn-google;
color: $sm-btn-google;
...
@@ -447,6 +455,19 @@ $sm-btn-linkedin: #0077b5;
...
@@ -447,6 +455,19 @@ $sm-btn-linkedin: #0077b5;
}
}
.button-secondary-login {
@extend %nav-btn-base;
@extend %t-action4;
@extend %t-regular;
border-color: $lightGrey1;
padding: 0;
height: $third-party-button-height;
&:hover {
border-color: $m-blue-d3;
}
}
/** Error Container - from _account.scss **/
/** Error Container - from _account.scss **/
.status {
.status {
@include box-sizing(border-box);
@include box-sizing(border-box);
...
@@ -503,6 +524,13 @@ $sm-btn-linkedin: #0077b5;
...
@@ -503,6 +524,13 @@ $sm-btn-linkedin: #0077b5;
}
}
}
}
.institution-list {
.institution {
@extend %t-copy-base;
}
}
@include media( max-width 330px) {
@include media( max-width 330px) {
.form-type {
.form-type {
width: 98%;
width: 98%;
...
...
lms/templates/student_account/access.underscore
View file @
7437bcfe
...
@@ -9,3 +9,7 @@
...
@@ -9,3 +9,7 @@
<section id="password-reset-anchor" class="form-type">
<section id="password-reset-anchor" class="form-type">
<div id="password-reset-form" class="form-wrapper hidden" aria-hidden="true"></div>
<div id="password-reset-form" class="form-wrapper hidden" aria-hidden="true"></div>
</section>
</section>
<section id="institution_login-anchor" class="form-type">
<div id="institution_login-form" class="form-wrapper hidden" aria-hidden="true"></div>
</section>
lms/templates/student_account/institution_login.underscore
0 → 100644
View file @
7437bcfe
<div class="wrapper-other-login">
<div class="section-title lines">
<h2>
<span class="text">
<%- gettext("Sign in with Institution/Campus Credentials") %>
</span>
</h2>
</div>
<p class="instructions"><%- gettext("Choose your institution from the list below:") %></p>
<ul class="institution-list">
<% _.each( _.sortBy(providers, "name"), function( provider ) {
if ( provider.loginUrl ) { %>
<li class="institution">
<a class="institution-login-link" href="<%- provider.loginUrl %>"><%- provider.name %></a>
</li>
<% }
}); %>
</ul>
<div class="section-title lines">
<h2>
<span class="text"><%- gettext("or") %></span>
</h2>
</div>
<div class="toggle-form">
<button class="nav-btn form-toggle" data-type="login"><%- gettext("Back to sign in") %></button>
</div>
</div>
lms/templates/student_account/institution_register.underscore
0 → 100644
View file @
7437bcfe
<div class="wrapper-other-login">
<div class="section-title lines">
<h2>
<span class="text">
<%- gettext("Register with Institution/Campus Credentials") %>
</span>
</h2>
</div>
<p class="instructions"><%- gettext("Choose your institution from the list below:") %></p>
<ul class="institution-list">
<% _.each( _.sortBy(providers, "name"), function( provider ) {
if ( provider.registerUrl ) { %>
<li class="institution">
<a class="institution-login-link" href="<%- provider.registerUrl %>"><%- provider.name %></a>
</li>
<% }
}); %>
</ul>
<div class="section-title lines">
<h2>
<span class="text"><%- gettext("or") %></span>
</h2>
</div>
<div class="toggle-form">
<button class="nav-btn form-toggle" data-type="register"><%- gettext("Register through edX") %></button>
</div>
</div>
lms/templates/student_account/login.underscore
View file @
7437bcfe
...
@@ -39,7 +39,7 @@
...
@@ -39,7 +39,7 @@
<button type="submit" class="action action-primary action-update js-login login-button"><%- gettext("Sign in") %></button>
<button type="submit" class="action action-primary action-update js-login login-button"><%- gettext("Sign in") %></button>
<% if ( context.providers.length > 0 && !context.currentProvider ) { %>
<% if ( context.providers.length > 0 && !context.currentProvider
|| context.hasSecondaryProviders
) { %>
<div class="login-providers">
<div class="login-providers">
<div class="section-title lines">
<div class="section-title lines">
<h2>
<h2>
...
@@ -55,6 +55,12 @@
...
@@ -55,6 +55,12 @@
</button>
</button>
<% }
<% }
}); %>
}); %>
<% if ( context.hasSecondaryProviders ) { %>
<button type="button" class="button-secondary-login form-toggle" data-type="institution_login">
<%- gettext("Use my institution/campus credentials") %>
</button>
<% } %>
</div>
</div>
<% } %>
<% } %>
</form>
</form>
...
...
lms/templates/student_account/login_and_register.html
View file @
7437bcfe
...
@@ -15,7 +15,7 @@
...
@@ -15,7 +15,7 @@
</
%
block>
</
%
block>
<
%
block
name=
"header_extras"
>
<
%
block
name=
"header_extras"
>
% for template_name in ["account", "access", "form_field", "login", "register", "password_reset"]:
% for template_name in ["account", "access", "form_field", "login", "register", "
institution_login", "institution_register", "
password_reset"]:
<script
type=
"text/template"
id=
"${template_name}-tpl"
>
<script
type=
"text/template"
id=
"${template_name}-tpl"
>
<%
static
:
include
path
=
"student_account/${template_name}.underscore"
/>
<%
static
:
include
path
=
"student_account/${template_name}.underscore"
/>
</script>
</script>
...
...
lms/templates/student_account/register.underscore
View file @
7437bcfe
...
@@ -19,7 +19,7 @@
...
@@ -19,7 +19,7 @@
<%- _.sprintf( gettext("We just need a little more information before you start learning with %(platformName)s."), context ) %>
<%- _.sprintf( gettext("We just need a little more information before you start learning with %(platformName)s."), context ) %>
</p>
</p>
</div>
</div>
<% } else if ( context.providers.length > 0 ) { %>
<% } else if ( context.providers.length > 0
|| context.hasSecondaryProviders
) { %>
<div class="login-providers">
<div class="login-providers">
<div class="section-title lines">
<div class="section-title lines">
<h2>
<h2>
...
@@ -35,6 +35,12 @@
...
@@ -35,6 +35,12 @@
</button>
</button>
<% }
<% }
}); %>
}); %>
<% if ( context.hasSecondaryProviders ) { %>
<button type="button" class="button-secondary-login form-toggle" data-type="institution_login">
<%- gettext("Use my institution/campus credentials") %>
</button>
<% } %>
</div>
</div>
<div class="section-title lines">
<div class="section-title lines">
<h2>
<h2>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment