diff --git a/app/assets/stylesheets/forms/_forms.sass b/app/assets/stylesheets/forms/_forms.sass index f3f635423..ad14b353a 100644 --- a/app/assets/stylesheets/forms/_forms.sass +++ b/app/assets/stylesheets/forms/_forms.sass @@ -91,6 +91,10 @@ hr margin-left: -1 * $form-spacing-horizontal margin-right: -1 * $form-spacing-horizontal +.actions + display: flex + justify-content: flex-end + .input flex: 1 1 600px display: flex diff --git a/app/assets/stylesheets/general/_header.sass b/app/assets/stylesheets/general/_header.sass index 99e1d8dae..1c09b3269 100644 --- a/app/assets/stylesheets/general/_header.sass +++ b/app/assets/stylesheets/general/_header.sass @@ -23,11 +23,19 @@ $account-nav-padding-vert: 15px .header-nav display: flex align-items: center + width: 100% .button margin-right: 10px &:last-child margin-right: 0 +.header-nav.start + justify-content: flex-start + flex-shrink: 2 + +.header-nav.end + justify-content: flex-end + .account-nav @include css4 border-bottom: 1px solid var(--account-nav--border-color) diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml new file mode 100644 index 000000000..a3742e61f --- /dev/null +++ b/app/views/doorkeeper/applications/_form.html.haml @@ -0,0 +1,12 @@ +.form-container + = bs_horizontal_simple_form_for(@application, url: doorkeeper_submit_path(@application)) do |f| + = f.error_notification + + .form-inputs + = f.input :name, required: true + = f.input :redirect_uri, required: true + = f.input :confidential + = f.input :scopes + + .center + = f.button :submit, class: 'btn-primary' diff --git a/app/views/doorkeeper/applications/edit.html.haml b/app/views/doorkeeper/applications/edit.html.haml new file mode 100644 index 000000000..e573e6e57 --- /dev/null +++ b/app/views/doorkeeper/applications/edit.html.haml @@ -0,0 +1,3 @@ += render "layouts/manage/page_title", title: @application.name, subtitle: t(:title, scope: 'doorkeeper.applications.edit') + += render 'form' diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml new file mode 100644 index 000000000..d856aa59e --- /dev/null +++ b/app/views/doorkeeper/applications/index.html.haml @@ -0,0 +1,29 @@ += render "layouts/manage/page_title", title: t(:title, scope: 'pages.manage.doorkeeper'), docs: 'https://coderit.org/hackathon-manager/docs/busses' do + = link_to t(:new, scope: 'doorkeeper.applications.index'), new_oauth_application_path, class: "btn btn-sm btn-outline-secondary" + +.mb-4 + %table.table.table-striped.table-hover + %thead + %tr + %th + %th + = t(:name, scope: 'doorkeeper.applications.index') + %th + = t(:callback_url, scope: 'doorkeeper.applications.index') + %th + = t(:confidential, scope: 'doorkeeper.applications.index') + + + %tbody + - @applications.each do |application| + %tr + %td + = link_to(''.html_safe, oauth_application_path(application)) + %td + %strong + = application.name + %td + = simple_format(application.redirect_uri) + %td + = application.confidential? ? t('doorkeeper.applications.index.confidentiality.yes') : t('doorkeeper.applications.index.confidentiality.no') + diff --git a/app/views/doorkeeper/applications/new.html.haml b/app/views/doorkeeper/applications/new.html.haml new file mode 100644 index 000000000..7818f0bef --- /dev/null +++ b/app/views/doorkeeper/applications/new.html.haml @@ -0,0 +1,3 @@ += render "layouts/manage/page_title", title: t(:title, scope: 'doorkeeper.applications.new') + += render 'form' diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml new file mode 100644 index 000000000..dfe2b1f0b --- /dev/null +++ b/app/views/doorkeeper/applications/show.html.haml @@ -0,0 +1,43 @@ += render "layouts/manage/page_title", title: @application.name, subtitle: t(:title, scope: 'doorkeeper.applications.show') do + .btn-group + = link_to t('doorkeeper.applications.buttons.edit'), edit_oauth_application_path(@application), class: 'btn btn-sm btn-outline-secondary' + = link_to t('doorkeeper.applications.buttons.destroy'), oauth_application_path(@application), method: :delete, data: { confirm: t('doorkeeper.applications.confirmations.destroy', name: @application.name) }, class: 'btn btn-sm btn-outline-secondary' + +.row + .col-lg-6 + %h4.border-bottom.pb-2.mb-3 Details + %dl.row + %dt.col-md-4 + = t('.application_id') + %dd.col-md-8 + %code.bg-light + = @application.uid + %dt.col-md-4 + = t('.secret') + %dd.col-md-8 + %code.bg-light + = flash[:application_secret].presence || @application.plaintext_secret + %dt.col-md-4 + = t('.scopes') + %dd.col-md-8 + - if @application.scopes.present? + = @application.scopes + - else + %bg-light.font-italic.text-muted + = t('.not_defined') + %dt.col-md-4 + = t('.confidential') + %dd.col-md-8 + = @application.confidential? ? "Yes" : "No" + + .col-lg-6 + .h4.border-bottom.pb-2.mb-3 + = t('.callback_urls') + %table.table.table-striped.table-hover + %tbody + - @application.redirect_uri.split.each do |uri| + %tr + %td + = uri + %td + = link_to t('doorkeeper.applications.buttons.authorize'), oauth_authorization_path(client_id: @application.uid, redirect_uri: uri, response_type: 'code', scope: @application.scopes), class: 'btn btn-sm btn-outline-primary', target: '_blank' diff --git a/app/views/doorkeeper/authorizations/error.html.haml b/app/views/doorkeeper/authorizations/error.html.haml new file mode 100644 index 000000000..e96f0e7a5 --- /dev/null +++ b/app/views/doorkeeper/authorizations/error.html.haml @@ -0,0 +1,10 @@ +.form-container + #disclaimer + %h1.section-title + Authorization + %span.emphasized Error + %p + = @pre_auth.error_response.body[:error_description] + %p + = raw t('doorkeeper.errors.messages.get_help', hackathon_name: content_tag(:strong, class: 'text-info') { HackathonConfig['name'] }) + diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml new file mode 100644 index 000000000..d1d73e20e --- /dev/null +++ b/app/views/doorkeeper/authorizations/new.html.haml @@ -0,0 +1,28 @@ +.form-container + #disclaimer + %h1.section-title + Authorization + %span.emphasized Required + %p + = raw t('.prompt', client_name: content_tag(:strong, class: 'text-info') { @pre_auth.client.name }) + %p + = t('.able_to') + .actions + = form_tag oauth_authorization_path, method: :delete, style: "padding-right: 10px;" do + = hidden_field_tag :client_id, @pre_auth.client.uid + = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri + = hidden_field_tag :state, @pre_auth.state + = hidden_field_tag :response_type, @pre_auth.response_type + = hidden_field_tag :scope, @pre_auth.scope + = hidden_field_tag :code_challenge, @pre_auth.code_challenge + = hidden_field_tag :code_challenge_method, @pre_auth.code_challenge_method + = submit_tag t('doorkeeper.authorizations.buttons.deny') + = form_tag oauth_authorization_path, method: :post do + = hidden_field_tag :client_id, @pre_auth.client.uid + = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri + = hidden_field_tag :state, @pre_auth.state + = hidden_field_tag :response_type, @pre_auth.response_type + = hidden_field_tag :scope, @pre_auth.scope + = hidden_field_tag :code_challenge, @pre_auth.code_challenge + = hidden_field_tag :code_challenge_method, @pre_auth.code_challenge_method + = submit_tag t('doorkeeper.authorizations.buttons.authorize') diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 91427e44d..3d591f229 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -6,9 +6,9 @@ = link_to homepage_url do = image_tag HackathonConfig['logo_asset'], id: 'logo', alt: "#{HackathonConfig['name']} logo", title: HackathonConfig['name'], class: 'header-logo__image' - else - .header-nav + .header-nav.start = btn_link_to "Home", homepage_url - .header-nav + .header-nav.end - if user_signed_in? - if current_user.organizing_staff? = btn_link_to "Manage", manage_root_path diff --git a/app/views/layouts/doorkeeper/_header.html.haml b/app/views/layouts/doorkeeper/_header.html.haml new file mode 100644 index 000000000..f8e581953 --- /dev/null +++ b/app/views/layouts/doorkeeper/_header.html.haml @@ -0,0 +1,9 @@ +- homepage_url = HackathonConfig['homepage_url'].presence || root_path +.header + .header__wrapper.account-nav__wrapper + - if HackathonConfig['logo_asset'].present? + .header-logo + = image_tag HackathonConfig['logo_asset'], id: 'logo', alt: "#{HackathonConfig['name']} logo", title: HackathonConfig['name'], class: 'header-logo__image' + .header-nav.end + - if user_signed_in? + = btn_link_to "Sign Out", destroy_user_session_path, method: :delete diff --git a/app/views/layouts/doorkeeper/application.html.haml b/app/views/layouts/doorkeeper/application.html.haml new file mode 100644 index 000000000..46e8b7222 --- /dev/null +++ b/app/views/layouts/doorkeeper/application.html.haml @@ -0,0 +1,27 @@ +!!! 5 +%html + %head + %title= yield(:title) || HackathonConfig['default_page_title'] + %meta{ charset: "utf-8" } + %meta{ name:"viewport", content: "width=device-width, initial-scale=1" } + + - if HackathonConfig['favicon_asset'].present? + %link{ href: image_url(HackathonConfig['favicon_asset']), rel: "shortcut icon" } + + = csrf_meta_tags + = csp_meta_tag + + = stylesheet_link_tag "application", media: "all", 'data-turbolinks-track': 'reload' + = javascript_include_tag "application", 'data-turbolinks-track': 'reload' + %link{ href:'//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700', rel: 'stylesheet', type: 'text/css' } + - if HackathonConfig['custom_css'].present? + %style + = HackathonConfig['custom_css'] + + %body + = render "layouts/doorkeeper/header" + = render "layouts/flashes" + #main + %section.section + .container + = yield diff --git a/app/views/layouts/manage/application.html.haml b/app/views/layouts/manage/application.html.haml index 9e716f056..98b542311 100644 --- a/app/views/layouts/manage/application.html.haml +++ b/app/views/layouts/manage/application.html.haml @@ -108,6 +108,12 @@ %span = t(:advanced, scope: 'layouts.manage.navigation') %ul.nav.flex-column.mb-2 + %li.nav-item + = active_link_to oauth_applications_path, class: "nav-link" do + .fa.fa-unlock.fa-fw.icon-space-r-half + = t(:title, scope: 'pages.manage.doorkeeper') + .nav-item-description + = t(:doorkeeper, scope: 'layouts.manage.navigation.descriptors') %li.nav-item = active_link_to sidekiq_web_path, target: '_blank', class: "nav-link" do .fa.fa-tasks.fa-fw.icon-space-r-half @@ -122,13 +128,6 @@ %span.fa.fa-external-link.icon-space-l-half .nav-item-description = t(:blazer, scope: 'layouts.manage.navigation.descriptors') - %li.nav-item - = active_link_to oauth_applications_path, target: '_blank', class: "nav-link" do - .fa.fa-unlock.fa-fw.icon-space-r-half - = t(:title, scope: 'pages.manage.doorkeeper') - %span.fa.fa-external-link.icon-space-l-half - .nav-item-description - = t(:doorkeeper, scope: 'layouts.manage.navigation.descriptors') %li.nav-item = active_link_to manage_data_exports_path, class: "nav-link" do .fa.fa-download.fa-fw.icon-space-r-half diff --git a/config/application.rb b/config/application.rb index 9e2bf1f19..227114d96 100644 --- a/config/application.rb +++ b/config/application.rb @@ -23,5 +23,13 @@ class Application < Rails::Application config.time_zone = ENV["TIME_ZONE"].presence || "UTC" config.active_job.queue_adapter = :sidekiq + + config.to_prepare do + # Only Applications list + Doorkeeper::ApplicationsController.layout "manage/application" + + # Only Authorized Applications + Doorkeeper::AuthorizedApplicationsController.layout "application" + end end end diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml index 0e5bbf2f4..df0440609 100644 --- a/config/locales/doorkeeper.en.yml +++ b/config/locales/doorkeeper.en.yml @@ -20,10 +20,10 @@ en: doorkeeper: applications: confirmations: - destroy: 'Are you sure?' + destroy: Are you sure? The application %{name} will be permanently erased. This action is irreversible. buttons: edit: 'Edit' - destroy: 'Destroy' + destroy: 'Delete' submit: 'Submit' cancel: 'Cancel' authorize: 'Authorize' @@ -49,24 +49,24 @@ en: new: title: 'New Application' show: - title: 'Application: %{name}' + title: 'Application' application_id: 'Application UID' secret: 'Secret' scopes: 'Scopes' confidential: 'Confidential' - callback_urls: 'Callback urls' + callback_urls: 'Callback URLs' actions: 'Actions' authorizations: buttons: - authorize: 'Authorize' + authorize: 'Allow' deny: 'Deny' error: title: 'An error has occurred' new: title: 'Authorization required' - prompt: 'Authorize %{client_name} to use your account?' - able_to: 'This application will be able to' + prompt: 'The application %{client_name} is requesting access to your information.' + able_to: 'This application will be able to access your personally identifiable information and application data.' show: title: 'Authorization code' @@ -87,7 +87,8 @@ en: errors: messages: # Common error messages - invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.' + invalid_request: + missing_param: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.' invalid_redirect_uri: "The requested redirect uri is malformed or doesn't match client redirect URI." unauthorized_client: 'The client is not authorized to perform this request using this method.' access_denied: 'The resource owner or authorization server denied the request.' @@ -96,6 +97,9 @@ en: server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.' temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.' + # Help from hackathon message + get_help: 'Please inform a %{hackathon_name} staff member of this issue for assistance.' + # Configuration error messages credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.' resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfigured.' diff --git a/config/locales/en.yml b/config/locales/en.yml index d3bd02c54..4e641763c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -194,7 +194,7 @@ en: blazer: title: Blazer doorkeeper: - title: Doorkeeper + title: App Authentication data-exports: title: Data Exports layouts: