yesod -- password protecting staging site - authentication

I'm trying to set up a staging instance of my yesod webserver, and I was wondering if there were some easy way to make the entire site password protected. Specifically, I want to be able to prompt those who navigate to my site for credentials. After they authenticate it should function as the typical site. But if they cannot authenticate themselves they should see nothing.

To expand on #MichaelSnoyman's answer, here's how I implemented the WAI HTTP Auth middleware:
From the scaffolded site, I went to Application.hs, which has already setup some logging middleware like so:
makeApplication :: AppConfig DefaultEnv Extra -> IO Application
makeApplication conf = do
foundation <- makeFoundation conf
-- Initialize the logging middleware
logWare <- mkRequestLogger def
{ outputFormat =
if development
then Detailed True
else Apache FromSocket
, destination = RequestLogger.Logger $ loggerSet $ appLogger foundation
}
-- Create the WAI application and apply middlewares
app <- toWaiAppPlain foundation
return $ logWare app
To add HTTP auth, I referenced the Yesod book's chapter on WAI and the HttpAuth docs that Michael referenced. The docs give this as an example of using the HttpAuth middleware:
basicAuth (\u p -> return $ u == "michael" && p == "mypass") "My Realm"
I was able to just paste that at the bottom right after the logging middleware is applied:
import qualified Network.Wai.Middleware.HttpAuth as HttpAuth
makeApplication :: AppConfig DefaultEnv Extra -> IO Application
makeApplication conf = do
foundation <- makeFoundation conf
-- Initialize the logging middleware
logWare <- mkRequestLogger def
{ outputFormat =
if development
then Detailed True
else Apache FromSocket
, destination = RequestLogger.Logger $ loggerSet $ appLogger foundation
}
-- Create the WAI application and apply middlewares
app <- toWaiAppPlain foundation
return $ logWare $ HttpAuth.basicAuth (\u p -> return $ u == "michael" && p == "mypass") "My Realm" $ app
Here's what that looks like in Safari:
This kind of authentication isn't really appropriate for regular users, but its great for locking down a site meant for internal use. Its also an easy way for machines (monitoring servers, scripts) to authenticate themselves with your server.

You could use the http auth middleware.
http://hackage.haskell.org/package/wai-extra-3.0.1/docs/Network-Wai-Middleware-HttpAuth.html
Sorry for brevity, on a mobile.

Related

UrlHelper returning http links on Azure App Service

I have a service that when deployed on Azure App Services returns http links instead of https links when using UrlHelper. When testing on my development machine it returns https links as expected, and the service is available and accessed through https requests.
An example of the type of route from my startup I'm trying to use is:
routes.MapRoute(
"FooBar",
"api/Foo/{Id}/Bar");
The link is then constructed using:
IUrlHelper _urlHelper = // Injected into class via service registration
int id = 42; // Arbitrary value for example
_urlHelper.Link("FooBar", new {Id = id});
When running on my local machine using Docker on Windows from Visual Studio I get a link of https://localhost:1234/api/Foo/42/Bar, but on my deployed Linux Container App Service on Azure I get http://my-app-name.azurewebsites.net/api/Foo/42/Bar.
I don't know what I'm doing wrong to get an http link instead of an https link, and would appreciate any advice/pointing in the right direction.
So I found the solution was with the configuration of the ASP.Net Core app itself. I performed the following modifications and then everything worked correctly:
Added app.UseForwardedHeaders(); to the request pipeline.
Added the following snippet to service container registration:
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
The KnownNetworks and KnownProxies need to be cleared as they default to assuming an IIS hosting environment. For extra security you can add the known proxy/network IPs instead of clearing them here.

Pass OS values to extJS app

How can I access external system values from within an ExtJS application? One of the main reasons I need to do this is that instead of doing authentication from my ExtJS application, I am using Apache to provide this to protected directories on my server, so I want to be able to pass some of the information about the user to the application, once authenticated and the app opened. It is far easier to use LDAP authentication in this manner...
Pass those values through:
Apache → Backend App → Generated HTML → Frontend App (Ext JS)
Your backend application, whatever technology it is built on, will see authentication information from Apache in environment variables. The actual variable names will depend on your setup. This is easily googlable as:
apache auth user environment variable <YOUR_BACKEND_TECHNOLOGY>
Then, the information obtained from Apache will need to be inserted in the HTML as JavaScript/JSON bit like this:
<script type="text/javascript">
var AUTH_INFO = {
"username": "John Smith"
// other information
};
</script>
Note that it needs to be inserted before Ext JS code.
Finally, in your Ext JS app, just access that info as a global variable:
AUTH_INFO.username

How to handle multiple auth strategies on a sails.js SPA?

How to handle multiple auth strategies on a sails.js SPA ?
I'm building a single page app built on Angular for the front-end and sailsjs for the backend. Right now I'm using sails-auth (which uses passportjs internally) to bind my authentication logic to my user model.
I have multiple passport providers installed and available on my frontend, such as passport-github and passport-facebook, but also a classic passport-local so that the user can also signup and login with just his username & password.
I would like my clients (The single page app, and maybe others in the future) to use a token after the auth instead of cookies/sessions so that it's easier to scale and cross-domain requests will also be easier. It will also make mobile integration much easier.
I know I have to use callbacks for OAuth providers, here is the flow that I'm aiming for :
I know that I can replace my sail-auth's sessionAuth policy by a tokenAuth policy that can read the token from the headers and query a Tokens model for example, but then my questions are :
When using username/password for login, the request can be made with a simple AJAX call so it is easy to pass the token back to the SPA. When using providers like github, etc., when the callback is called, should I just embed the token dynamically into the HTML that I'm serving?
sail-auth's policies/passport.js shows that by default it relies on built-in sessions to persist login/to serialize&deserialize the userID. How do I decouple it from sails built-in sessions so that it generates a token for the user and serve back my index with the token embedded?
Thank you in advance!
On the auth route, you could go for passport.js based authentication in the backend (without session), use the token for tokenAuth and forward the token to the user.
Then for secure routes, you could place verifyToken call in your policy (intercept each route).
Disclaimer: I haven't tried this myself.
Ive been using these steps for a while now.
Step 1 ( Globals ): $ npm install -g sails
Step 2 ( App ): $ sails new myApp
Step 3 ( Files ): Copy every file in https://github.com/carlospliego/sails-token-auth-setup to its corresponding folder
Step 3A To have another authentication strategy just add another file in the app/policies/ directory
Here is an example of what that might look like
module.exports = function hasValidProductApiToken(req, res, next) {
if(someCondition){
next(); // Call next to continue
}
};
Step 4 ( Policies ): Add this code to your config/policies.js
'*': "hasToken",
UserController: {
"create": true
},
AuthController: {
'*': true
}
Step 5: change the value of config/tokenSecret.js
Step 6: ( Dependencies )
npm install --save passport
npm install --save passport-local
npm install --save bcrypt-nodejs
npm install --save jsonwebtoken
npm install --save express-jwt
Your endpoints will look like this:
POST/GET/PUT/DELETE user/
POST auth/login
DELETE auth/logout
Here is a great guide on how to create token based authentication in sails: https://github.com/carlospliego/sails-token-auth-setup

Admin Tools config throws 404: page not found

I try to setup the basic configuration of admin tools and fail with the url dispatcher recognizing the include of admin_tools.urls:
#urls.py
...
import admin_tools.urls
urlpatterns = patterns('',
url(r'^admintools/', include(admin_tools.urls)),
url(r'^admin/', include(admin.site.urls)),
)
#settings.py
import django.conf.global_settings as DEFAULT_SETTINGS
TEMPLATE_CONTEXT_PROCESSORS = DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS + (
"django.core.context_processors.request",
)
INSTALLED_APPS = (
'django.contrib.auth',
...
'admin_tools',
'admin_tools.menu',
'admin_tools.dashboard',
'django.contrib.admin',
'repmgr',)
I did run syncdb. I am sure that the regexp matches /admintools/, because it works when I include another app.
The detailed error response is:
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/admintools/
Using the URLconf defined in urlconf, Django tried these URL patterns, in this order:
^__debug__/m/(.*)$
^__debug__/sql_select/$ [name='sql_select']
^__debug__/sql_explain/$ [name='sql_explain']
^__debug__/sql_profile/$ [name='sql_profile']
^__debug__/template_source/$ [name='template_source']
^admin/doc/
^admintools/ ^menu/
^admintools/ ^dashboard/
^sthrep/
The current URL, admintools/, didn't match any of these.
The error message is perfectly correct :
Django tells that there is no URL pattern matching ^admintools/^. That is true. This is because django-admin-tools does not create a different admin site, but rather extends the original admin site.
The ^admintools/ pattern is only created for its sub-patterns, that provide access to pages needed by admin-tools to work well (new pages added to the original admin, for example).
By the way, in django-admin-tools documentation, at the end of the setup instructions, they say :
Congrats! At this point you should have a working installation of
django-admin-tools. Now you can just login to your admin site and see
what changed.
I guess that by your admin site they mean the standard admin site.
So I think that the good way to access your admin interface and menus and dashboards is using the standard admin.

Cannot get service account authorization to work on GCS script using Python client lib APIs

In attempting to write a python script to access GCS using service-based authorization, I have come up with the following. Note that 'key' is the contents of my p12 file.
I am attempting to just read the list of buckets on my account. I have successfully created one bucket using the web interface to GCS, and can see that with gsutil.
When I execute the code below I get a 403 error. At first I thought I was not authorized correctly, but I tried from this very useful web page (which uses web-based authorization), and it works correctly. https://developers.google.com/apis-explorer/#p/storage/v1beta1/storage.buckets.list?projectId=&_h=2&
When I look at the headers and query string and compare them to the keaders and query of the website-generated request I see that there is no authorization header, and that there is no key= tag in the query string. I suppose I thought that the credential authorization would have taken care of this for me.
What am I doing wrong?
code:
credentials = SignedJwtAssertionCredentials(
'xxx-my-long-email-from-the-console#developer.gserviceaccount.com',
key,
scope='https://www.googleapis.com/auth/devstorage.full_control')
http = httplib2.Http()
http = credentials.authorize(http)
service = build("storage", "v1beta1", http=http)
# Build the request
request = service.buckets().list(projectId="159910083329")
# Diagnostic
pprint.pprint(request.headers)
pprint.pprint(request.to_json())
# Do it!
response = request.execute()
When I try to execute I get the 403.
I got this working, however, the code I used is not fundamentally different from the snippet you posted. Just in case you'd like to diff my version with yours, attached below is a complete copy of a Python program that worked for me. I initially got a 403, just like you, which was due to inheriting your project id :). After updating that value to use my project ID, I got a correct bucket listing. Two things to check:
Make sure the project id you are using is correct and has the "Google Cloud Storage JSON API" enabled on the Google Developer Console "Services" tab (it's a different service from the other Google Cloud Storage API).
Make sure you are loading the service accounts private key exactly as it came from the developer's console. I would recommend reading it into memory from the file you downloaded, as I've done here, rather than trying to copy it into a string literal in your code.
#!/usr/bin/env python
import pprint
import oauth2client
from oauth2client.client import SignedJwtAssertionCredentials
import httplib2
from apiclient.discovery import build
f = open('key.p12', 'r')
key = f.read()
f.close()
credentials = SignedJwtAssertionCredentials(
'REDACTED',
key,
scope='https://www.googleapis.com/auth/devstorage.full_control')
http = httplib2.Http()
http = credentials.authorize(http)
service = build("storage", "v1beta1", http=http)
# Build the request
request = service.buckets().list(projectId="REDACTED")
# Diagnostic
pprint.pprint(request.headers)
pprint.pprint(request.to_json())
# Do it!
response = request.execute()
pprint.pprint(response)