#196 Search option to send Happiness Packet to specific FAS account!
Merged 5 years ago by jflory7. Opened 5 years ago by alishapapun.
fedora-commops/ alishapapun/fedora-happiness-packets fasid-email  into  master

file modified
+3
@@ -24,3 +24,6 @@ 

  

  #OIDC credentials

  client_secrets.json

+ 

+ #Adding fas-admin-details.json so that the username and password does not get pushed

+ fas-admin-details.json

Since this file is still committed, if changes are made, the changes can still be committed. It is better practice to provide an example file that someone can copy into the "correct" file for the app to read. For example: fas-admin-config.json.example Then, this line in the gitignore file will work correctly.

file modified
+9
@@ -324,3 +324,12 @@ 

  .socio-buttons a {

      padding: 0.9rem;

  }

+ .row .error{

+   color: red;

+   font-size: 1.4rem;

+   text-align: center;

+ }

+ .row .searching-text{

+   text-align: center;

+   font-size:1.4rem;

+ }

@@ -0,0 +1,64 @@ 

+ $(document).ready(function () {

+   $("#id_fasid").val('')

+   $("#id_recipient_name").val('')

+   $("#id_recipient_email").val('')

+ 

+   $("#id_fasid").change(function () {

+ 

+     let fasid = $('#id_fasid').val()

+     $("#server-error").remove()

+ 

+     if (fasid != '') {

+ 

+       $("#id_recipient_name").attr("placeholder", "")

+       $("#id_recipient_name").val('')

+       $("#id_recipient_email").val('')

+       $("#id_fasid").prop('disabled', true);

+       $("#no-fas-id-error").remove()

+       $("#server-error").remove()

+       $("#id_fasid").after('<p class="searching-text">Searching for FAS Username.........</p>')

+ 

+       email = $.get('/send/search', { fasid: fasid }, function (data) {

+ 

+         if (data['server_error'] == 'True') {

+           $("#id_fasid").val('')

+           $('#server-error').remove()

+           $('.searching-text').remove()

+           $("#id_fasid").prop('disabled', false);

+           $("#id_fasid").after('<p class="error" id="server-error" >Internal Server Error Occured! Enter Name and Email manually!</p>')

+           $('#server-error').fadeIn('slow', function () {

+             $('#server-error').delay(3000).fadeOut()

+           })

+         }

+         else {

+           $('.error').remove()

+           $('.searching-text').remove()

+           $("#id_fasid").prop('disabled', false);

+ 

+           if (data['account_exists'] == 'Yes') {

+ 

+             if (data['name'] == 'No name') {

+               $("#id_recipient_name").attr("placeholder", "Privacy is Set! Type Name Manually")

+             }

+ 

+             else {

+               $("#id_recipient_name").val(data['name'])

+             }

+ 

+             $("#id_recipient_email").val(data['email'])

+           }

+           else {

+             $("#no-fas-id-error").remove()

+             $("#id_fasid").after('<p class="error" id="no-fas-id-error">Sorry! No such FAS Username exsist</p>')

+             console.log('No such FAS username exsists')

+             $('#no-fas-id-error').fadeIn('slow', function () {

+               $('#no-fas-id-error').delay(2700).fadeOut()

+             })

+             $("#id_fasid").val('')

+           }

+           return data;

+         }

+       })

+     }

+   })

+ });

@@ -32,6 +32,8 @@ 

  

      ./generate_client_secrets.sh

  

+ #. Create a fas-admin-details.json file and add a json object with your FAS-Username and FAS-Password. See fas-admin-details.json.example.

+ 

  Although the Dockerfile runs the script to check if a client_secrets.json file is present, please generate it before starting the Docker container, so that client secrets are not being constantly generated every time the image is rebuilt.

  

  In order to run the web server, alongside the Redis queue and celery worker instance, simply run ``docker-compose up``.

@@ -0,0 +1,5 @@ 

+ #Create fas-admin-details.json and store the following json object into the file with your actual credentials. 

+ {

+ 	"ADMIN_USERNAME":"<admin-username>",

+ 	"ADMIN_PASSWORD":"<admin-password>"

+ }

@@ -27,6 +27,7 @@ 

  

  class MessageSendForm(forms.ModelForm):

      # hp = forms.CharField(label="do not fill", required=False)

+     fasid = forms.CharField(label="FAS Username", required=False)

  

      class Meta:

          model = Message
@@ -52,6 +53,7 @@ 

  

          self.helper.layout = Layout(

              # Fieldset('This Happiness Packet is from...', 'sender_name', 'sender_email', 'hp'),

+             Fieldset("Search for a FAS Username", 'fasid' ),

              Fieldset("Send this Happiness Packet to...", 'recipient_name', 'recipient_email'),

              Fieldset("Your message is...", 'message'),

              Fieldset("Privacy and permissions", 'sender_named', 'sender_approved_public', 'sender_approved_public_named'),

@@ -4,7 +4,7 @@ 

  

  from .views import (StartView, MessageSearchView, MessageSendView, MessageSenderConfirmationSentView, MessageSenderConfirmationView,

                      MessageSenderConfirmedView, MessageRecipientMessageUpdate, FaqView, ArchiveView, InspirationView,

-                     BlacklistEmailView, ReceivedMessagesView, SentMessagesView)

+                     BlacklistEmailView, ReceivedMessagesView, SentMessagesView, FasidSearchView)

  

  app_name = 'messaging'

  
@@ -21,5 +21,6 @@ 

      re_path(r'^send/confirmation/(?P<identifier>[\w-]+)/(?P<token>[\w-]+)/$', MessageSenderConfirmationView.as_view(), name='sender_confirm'),

      re_path(r'^send/confirmed/$', MessageSenderConfirmedView.as_view(), name='sender_confirmed'),

      re_path(r'^recipient/(?P<identifier>[\w-]+)/(?P<token>[\w-]+)/$', MessageRecipientMessageUpdate.as_view(), name='recipient_message_update'),

+     re_path(r'^send/search/$', FasidSearchView.fasidCheck, name='fasid_check'),

      re_path(r'^search/?$', MessageSearchView.as_view(), name='search'),

  ]

@@ -5,7 +5,7 @@ 

  

  from django.contrib import messages

  from django.urls import reverse, reverse_lazy

- from django.http import HttpResponseRedirect, Http404

+ from django.http import HttpResponseRedirect, Http404, JsonResponse

  from django.utils.crypto import salted_hmac, constant_time_compare

  from django.utils.decorators import method_decorator

  from django.utils.html import format_html
@@ -14,6 +14,7 @@ 

  from django.contrib.auth.mixins import LoginRequiredMixin

  from django.db.models import Q

  from django.shortcuts import render

+ from django.conf import settings

  

  from haystack.generic_views import SearchView

  from haystack.forms import SearchForm
@@ -26,6 +27,10 @@ 

  from fedora_messaging.exceptions import PublishReturned, ConnectionException

  from happinesspacket_schema.schema import MessageV1

  

+ #Include python-fedora

+ from fedora.client.fas2 import AccountSystem

+ from fedora.client import AuthError

+ 

  logger = logging.getLogger(__name__)

  

  class MessageSearchView(SearchView):
@@ -141,7 +146,7 @@ 

                  "id": message.identifier,

                  "sender": sender_name,

                  "recipient": message.recipient_name

-             }    	         

+             }

          )

          try:

              publish(message)
@@ -199,3 +204,33 @@ 

      def get_queryset(self):

          queryset = super(SentMessagesView, self).get_queryset()

          return queryset.filter(sender_email=self.request.user.email)

+ 

+ class FasidSearchView():

+     @staticmethod

+     def fasidCheck(request):

+         try:

+             fas = AccountSystem(username= settings.ADMIN_USERNAME, password= settings.ADMIN_PASSWORD)

+             fasid = request.GET['fasid']

+             is_server_error = 'False'

+             type_of_error = ' No Error '

+             person = fas.person_by_username(fasid)

+             u_name = 'No name'

+             u_email = 'No email'

+             if not person:

+                 logger.error("The FAS username doesnot exsist!")

+                 account_exists = 'No'

+             else:

+                 account_exists = 'Yes'

+                 privacy = person['privacy']

+                 if not(privacy):

+                     logger.warn("The privacy is set to not view the Name!")

+                     u_name = person['human_name']

+                 u_email = person['email']

+ 

+             context = {'account_exists':account_exists,'email': u_email, 'name': u_name, 'server_error': is_server_error, 'type_of_error': type_of_error}

+         except Exception as ex:

+             type_of_error = ex.__class__.__name__

+             logger.error("%s Occured", type_of_error)

+             is_server_error = 'True'

+             context = {'account_exists':'Can\'t Say','email': 'Can\'t Say', 'name': 'Can\'t Say', 'server_error': is_server_error, 'type_of_error': type_of_error}

+         return JsonResponse(context)

@@ -130,6 +130,10 @@ 

  OIDC_RP_IDP_SIGN_KEY = '-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAq/0/XjILQxF3OaQZtFE3wVJ5UUuxZbxiJ/z+Zai0EOHiaMMxVyoo\nibDRen615r525DQ8TmQyR0eMQEpQ6SUvaOunahpYohgAkbkYggUMQhcoCLme18ZJ\nBTNWTP8w4t7mcuZd1cy1KtHpEvH4gkrjp8N3vIv1lzFraSc+p2rHMbV+AX5CJQ1H\nohBdwaqyOBKp0nzY27gu2EH2vzCwXkO4zGtrHfjjGc0Ra4WG+xz1AWg833xcFj3p\nqM3vca09jDLBme+GT151LcCCXRNyOZPZ3ZX62NxkMyqvVJHC3Uu2Q1hSHO7f6AZk\nZXY88PXXEH52T2ZrWiISowjTcGUboP8goQIDAQAB\n-----END RSA PUBLIC KEY-----\n'

  OIDC_RP_CLIENT_ID = os.environ.get('OIDC_RP_CLIENT_ID')

  OIDC_RP_CLIENT_SECRET = os.environ.get('OIDC_RP_CLIENT_SECRET')

+ 

+ ADMIN_USERNAME = os.getenv('ADMIN_USERNAME')

+ ADMIN_PASSWORD = os.getenv('ADMIN_PASSWORD')

+ 

  OIDC_OP_AUTHORIZATION_ENDPOINT = "https://iddev.fedorainfracloud.org/openidc/Authorization"

  OIDC_OP_TOKEN_ENDPOINT = "https://iddev.fedorainfracloud.org/openidc/Token"

  OIDC_OP_USER_ENDPOINT = "https://iddev.fedorainfracloud.org/openidc/UserInfo"

@@ -83,4 +83,8 @@ 

      OIDC_RP_CLIENT_ID = secrets["client_id"]

      OIDC_RP_CLIENT_SECRET = secrets["client_secret"]

  

- 

+ # Reading the fas-id and Password

+ with open("fas-admin-details.json") as f:

+     secrets = json.load(f)

+     ADMIN_USERNAME = secrets["ADMIN_USERNAME"]

+     ADMIN_PASSWORD =  secrets["ADMIN_PASSWORD"]

file modified
+2
@@ -42,3 +42,5 @@ 

  Whoosh==2.7.4

  django-haystack==2.8.1

  

+ #python-fedora for f-a-s API

+ python-fedora==0.10.0

file modified
+2
@@ -13,6 +13,8 @@ 

      <link href="{% static 'css/custom.css' %}" rel="stylesheet">

      <link href="{% static 'images/favicon.ico' %}" rel="icon" type="image/x-icon">

      <script src="https://apis.google.com/js/platform.js" async defer></script>

+     <script src="http://code.jquery.com/jquery-latest.min.js"></script>

+     <script src="{% static 'js/fas_details.js' %}" type="text/javascript"></script>

  

      <!--[if lt IE 8]>

          <link href="{% static 'css/bootstrap-ie7.css' %}" rel="stylesheet">

@@ -5,6 +5,7 @@ 

  

      <div class="page-header">

          <h2>Send a happiness packet to someone!</h2>

+         <br>

      </div>

  

      {% crispy form %}

This is a PR regarding #127.
(Here the admin-username can the username of any authenticated fas-user and their password)

1 new commit added

  • Resized the font for searching
5 years ago

3 new commits added

  • Resized the font
  • Adding fas-admin-details.json so that the username and password does not get pushed
  • Retrive email and name from fas-id
5 years ago

Metadata Update from @jflory7:
- Pull-request tagged with: PASSED, needs testing, new change, type - backend, type - summer coding

5 years ago

1 new commit added

  • Read FAS credentials from env vars
5 years ago

rebased onto 45d14775fde38cb285b21b8bd71b5b3b0b6eef7b

5 years ago

Since this file is still committed, if changes are made, the changes can still be committed. It is better practice to provide an example file that someone can copy into the "correct" file for the app to read. For example: fas-admin-config.json.example Then, this line in the gitignore file will work correctly.

This is a nitpick, but for what we present to the user, I think we should stick to "FAS username" instead of "FAS-ID". I believe it will better recognized if written like this.

No such FAS-ID exsist => No such FAS username exists.

I was confused initially because I tried searching for someone's email address to look-up their name. I think we should reword this, maybe something like Search for a FAS username. What do you think?

Minor typo. accountExsist => account_exists

I suggest naming this file fas-admin-config.json.example so it is not accidentally checked into git by mistake.

@alishapapun Excellent work with this PR! :tada: :tada: This is awesome to see. :smile:

I left some in-line comments above, but they are mostly small details. There is one issue I ran into though. When I tested the application with no FAS credentials, there isn't error handling for when there are incorrect credentials provided:

web_1       | [19/Mar/2019 02:04:57] "GET /send/search?fasid=jflo HTTP/1.1" 301 0
web_1       | Internal Server Error: /send/search/
web_1       | Traceback (most recent call last):
web_1       |   File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner
web_1       |     response = get_response(request)
web_1       |   File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 128, in _get_response
web_1       |     response = self.process_exception_by_middleware(e, request)
web_1       |   File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
web_1       |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
web_1       |   File "/usr/local/lib/python3.6/contextlib.py", line 52, in inner
web_1       |     return func(*args, **kwds)
web_1       |   File "/app/happinesspackets/messaging/views.py", line 213, in fasidCheck
web_1       |     person = fas.person_by_username(fasid)
web_1       |   File "/usr/local/lib/python3.6/site-packages/fedora/client/fas2.py", line 445, in person_by_username
web_1       |     req_params=params)
web_1       |   File "/usr/local/lib/python3.6/site-packages/fedora/client/baseclient.py", line 367, in send_request
web_1       |     auth_params=auth_params, retries=retries, timeout=timeout)
web_1       |   File "/usr/local/lib/python3.6/site-packages/fedora/client/proxyclient.py", line 462, in send_request
web_1       |     'Unable to log into server.  Invalid'
web_1       | fedora.client.AuthError: Unable to log into server.  Invalid authentication tokens.  Send new username and password

Screenshot of "Send some happiness!" page after multiple searches when FAS admin credentials are missing

It would be helpful to make it more clear to the user if there is a configuration error and they should be blocked from making a new search while one is already in progress. Do you think you could add some error handling for invalid authentication credentials in the back-end and front-end?

Metadata Update from @jflory7:
- Pull-request untagged with: needs testing
- Pull-request tagged with: needs changes
- Request assigned

5 years ago

1 new commit added

  • Error handelling for FAS username search failure
5 years ago

@alishapapun Excellent work with this PR! 🎉 🎉 This is awesome to see. 😄
I left some in-line comments above, but they are mostly small details. There is one issue I ran into though. When I tested the application with no FAS credentials, there isn't error handling for when there are incorrect credentials provided:
web_1 | [19/Mar/2019 02:04:57] "GET /send/search?fasid=jflo HTTP/1.1" 301 0
web_1 | Internal Server Error: /send/search/
web_1 | Traceback (most recent call last):
web_1 | File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner
web_1 | response = get_response(request)
web_1 | File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 128, in _get_response
web_1 | response = self.process_exception_by_middleware(e, request)
web_1 | File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
web_1 | response = wrapped_callback(request, *callback_args, callback_kwargs)
web_1 | File "/usr/local/lib/python3.6/contextlib.py", line 52, in inner
web_1 | return func(*args,
kwds)
web_1 | File "/app/happinesspackets/messaging/views.py", line 213, in fasidCheck
web_1 | person = fas.person_by_username(fasid)
web_1 | File "/usr/local/lib/python3.6/site-packages/fedora/client/fas2.py", line 445, in person_by_username
web_1 | req_params=params)
web_1 | File "/usr/local/lib/python3.6/site-packages/fedora/client/baseclient.py", line 367, in send_request
web_1 | auth_params=auth_params, retries=retries, timeout=timeout)
web_1 | File "/usr/local/lib/python3.6/site-packages/fedora/client/proxyclient.py", line 462, in send_request
web_1 | 'Unable to log into server. Invalid'
web_1 | fedora.client.AuthError: Unable to log into server. Invalid authentication tokens. Send new username and password

This error occurred because you have not changed the ADMIN_USERNAME and ADMIN_PASSWORD in the fas-admin-details.json to any authenticated username and email ( Invalid authentication tokens refers to the wrong credentials passed to the api of FAS which is read from the fas-admin-details.json ). After you provide proper username and password, there is proper error handling by showing an error message of username does not exsist.

Screenshot of "Send some happiness!" page after multiple searches when FAS admin credentials are missing
It would be helpful to make it more clear to the user if there is a configuration error and they should be blocked from making a new search while one is already in progress. Do you think you could add some error handling for invalid authentication credentials in the back-end and front-end?

I have made the required changes in the project. Could you please review the changes ? If everything is all right I would just squash my commits into one. Thanks :)

I have made the required changes in the project. Could you please review the changes ? If everything is all right I would just squash my commits into one. Thanks :)

I had a chance to test this now. The changes are great! Thanks for getting on this so quickly.

I think this is almost ready to merge. I'm requesting two additions and two new tickets.

  1. :heavy_plus_sign: Addition: On an instance with an incorrect/missing FAS username/password, when someone makes a search, some sort of one-line error message should go out on the console too.
  2. :heavy_plus_sign: Addition: Quick note in developer environment guide about copying fas-admin-details.json.example to fas-admin-details.json (note you don't need to update the Windows setup guide, see this comment for why).
  3. :ticket: New ticket: Add a search button in case pressing Return is not intuitive to user (see screenshot mockup below)
  4. :ticket: New ticket: Predict usernames with auto-completion in popup dialog while typing in FAS username search bar

Mock-up screenshot of a "Search" button in FAS Username search field

Moving 3 and 4 into new tickets keeps future improvements in mind and lets us move forward with merging this. If you can add error logging to the console and a note in the development environment setup guide, this is ready to merge! :tada:

Thanks again for your work on this PR. :smile:

2 new commits added

  • Add details of setup of search in the documentation
  • Add error message in the console while searching for FAS username
5 years ago

rebased onto 22f3716f8957133fa89d7ae8393078aa54f22c00

5 years ago

rebased onto 45d14775fde38cb285b21b8bd71b5b3b0b6eef7b

5 years ago

7 new commits added

  • Add details of setup for search in the Documentation
  • Add error message in the console while searching for FAS username
  • Error handelling for FAS username search failure
  • Read FAS credentials from env vars
  • Resized the font
  • Adding fas-admin-details.json so that the username and password does not get pushed
  • Retrive email and name from fas-id
5 years ago

1 new commit added

  • Resolve merge conflicts
5 years ago

Hey @jflory7 ! Sorry for piling up bunch of commit message here. I ran into some problem and one problem lined to another while solving the merge conflict. But finally sorted it out. I made the changed that you asked and cleared up the merged conflict that was taking place. It would be great if you could merge the code after testing it ! Thanks :)

Shall I squash the commits or its fine? (If squashing is required, which particular commits you want to squash) ? @jflory7

It would be great if you could merge the code after testing it ! Thanks :)

Thanks @alishapapun! :thumbsup: I just tested it but I wasn't able to verify the console log message. I hoped a message would appear in the docker-compose console for the web container. When I made a search with invalid FAS credentials entered, I only saw this output in console:

web_1 | [22/Mar/2019 00:50:45] "GET /send/search/?fasid=alishapapun HTTP/1.1" 200 128

I was hoping to see an error message to indicate why the search failed (i.e. the FAS login credentials are incorrect).

Shall I squash the commits or its fine? (If squashing is required, which particular commits you want to squash) ? @jflory7

My preference is to squash commits at the end right before merging (it makes it easier to review between giving feedback). When it is ready to merge, squash the commits down to one or two meaningful commits. :smile:

1 new commit added

  • Add console message for search failure
5 years ago

It would be great if you could merge the code after testing it ! Thanks :)

Thanks @alishapapun! 👍 I just tested it but I wasn't able to verify the console log message. I hoped a message would appear in the docker-compose console for the web container. When I made a search with invalid FAS credentials entered, I only saw this output in console:
web_1 | [22/Mar/2019 00:50:45] "GET /send/search/?fasid=alishapapun HTTP/1.1" 200 128
I was hoping to see an error message to indicate why the search failed (i.e. the FAS login credentials are incorrect).

Shall I squash the commits or its fine? (If squashing is required, which particular commits you want to squash) ? @jflory7

My preference is to squash commits at the end right before merging (it makes it easier to review between giving feedback). When it is ready to merge, squash the commits down to one or two meaningful commits. 😄

Made the changes you asked for @jflory7 . :)

Made the changes you asked for @jflory7 . :)

Super! :thumbsup: I tested this and it worked as expected. This is ready to merge! :tada:

Please squash your commits down to 1 or 2 meaningful commits and then I'll merge. :smile: When you have time, don't forget about the two new tickets I mentioned here.

rebased onto 00494df71fd6baec131a702b978f69718bc0a705

5 years ago

Made the changes you asked for @jflory7 . :)

Super! 👍 I tested this and it worked as expected. This is ready to merge! 🎉
Please squash your commits down to 1 or 2 meaningful commits and then I'll merge. 😄 When you have time, don't forget about the two new tickets I mentioned here.

Hey @jflory7 ! Squashed my commits to one. And yes I remember these two tickets. Will surely proceed with it :thumbsup:

rebased onto da4da03

5 years ago

@alishapapun Awesome! We're all set here. Merging! :clapper:

Pull-Request has been merged by jflory7

5 years ago

@alishapapun Awesome! We're all set here. Merging! 🎬

The best feeling ever! :heart:

Metadata Update from @jflory7:
- Pull-request untagged with: needs changes

5 years ago

🎫 New ticket: Add a search button in case pressing Return is not intuitive to user (see screenshot mockup below)
🎫 New ticket: Predict usernames with auto-completion in popup dialog while typing in FAS username search bar

Hi @alishapapun, did you have a chance to file these two issues? Let me know if you want to do this or if you prefer for me to file them. I want to make sure we keep track of these two tasks. :smiley: