Giter Club home page Giter Club logo

friend's Introduction

This library is currently unmaintained. You might want to investigate forks for additional enhancements, etc., or alternative approaches generally (e.g. I hear people like Buddy)

Friend Travis CI status

An extensible authentication and authorization library for Clojure/Ring web applications and services.

Picking up his staff he stood before the rock and said in a clear voice:
Mellon!

The star shone out briefly and faded again. Then silently a great
doorway was outlined, though not a crack or joint had been visible
before. Slowly it divided in the middle and swung outwards inch by inch,
until both doors lay back against the wall. Through the opening a
shadowy stair could be seen climbing steeply up; but beyond the lower
steps the darkness was deeper than the night. The Company stared in
wonder.

"I was wrong after all," said Gandalf, "and Gimli too. Merry, of all
people, was on the right track. The opening word was inscribed on the
archway all the time! The translation should have been: Say 'Friend' and
enter. I had only to speak the Elvish word for friend and the doors
opened. Quite simple. Too simple for a learned lore master in these
suspicious days. Those were happier times. Now let us go!"

— J.R.R. Tolkien, Lord of the Rings

Overview

Friend is intended to provide a foundation for addressing all of the authentication and authorization concerns associated with web apps:

  • user agent authentication; Friend currently includes support for form and HTTP Basic authentication, and makes it easy to:
    • implement and use other workflows (e.g oauth, OpenId connect)
    • integrate app-specific user-credential checks
  • role-based authorization
    • optionally uses Clojure's ad-hoc hierarchies to model hierarchical roles
  • su capabilities (a.k.a. "log in as"), enabling users to maintain multiple simultaneous logins, as well as to allow administrators to take on users' identities for debugging or support purposes (in progress)
  • and the creature comforts:
    • Ring middlewares for configuring and defining the scopes of authentication and authorization
    • Macros to clearly demarcate the scope of authentication and authorization within code that is "below" the level of Ring handlers where you can't use middlewares.
    • A reasonable Clojure API around the jbcrypt library for hashing sensitive bits.
    • Enables DRY routes and configuration, e.g. no need to configure your routes in Compojure or Noir or Moustache, and separately specify which routes fall under the jurisdiction of Friend's security machinery
    • Purely functional in nature: authentications, roles, and session data are obtained, retained, and passed around as good ol' persistent data structures (just as Ring intended). No stateful session or context is ever bashed in place, making it easier to reason about what's going on.

Why?

Nothing like Friend exists, and it needs to. Securing Ring applications and services is (charitably speaking) a PITA right now, with everyone rolling their own, or starting with relatively low-level middlewares and frameworks. This will never do. Serious web applications need to take security seriously, and need to readily interoperate with all sorts of authentication mechanisms that have come to litter the web as well as internal networks.

Friend has been built with one eye on a number of frameworks.

Status

Very stable, widely-used in production AFAIK.

Note: while actively maintained, Friend is in search of a new maintainer.

Changelog

Available here.

Known issues

  • This README is way too long and not well-organized. It's more of a brain-dump than anything else at the moment.
  • Configuration keys need a bit of tidying, especially for those that can/should apply to multiple authorization workflows. Fixes for such things will break the existing API.
  • the su mechanism is in-progress
  • …surely there's more. File issues.

"Installation"

Friend is available in Clojars. Add this :dependency to your Leiningen project.clj:

[com.cemerick/friend "0.2.3"]

Or, add this to your Maven project's pom.xml:

<repository>
  <id>clojars</id>
  <url>http://clojars.org/repo</url>
</repository>

<dependency>
  <groupId>com.cemerick</groupId>
  <artifactId>friend</artifactId>
  <version>0.2.3</version>
</dependency>

Friend is compatible with Clojure 1.2.0 - 1.5.0+.

Usage

How you use Friend will vary, sometimes significantly, depending on the authentication providers you use and the authorization policy/ies you want to enforce. A generic example of typical usage of Friend is below, but the best way to become familiar with Friend and how it can be used would be to go check out

…a collection of tiny demonstration apps using Friend. It should be easy to find the one(s) that apply to your situation, and go straight to its source so you can see how all the pieces fit together.


Here's probably the most self-contained Friend usage possible:

(ns your.ring.app
  (:require [cemerick.friend :as friend]
            (cemerick.friend [workflows :as workflows]
                             [credentials :as creds])))

; a dummy in-memory user "database"
(def users {"root" {:username "root"
                    :password (creds/hash-bcrypt "admin_password")
                    :roles #{::admin}}
            "jane" {:username "jane"
                    :password (creds/hash-bcrypt "user_password")
                    :roles #{::user}}})

(def ring-app ; ... assemble routes however you like ...
  )

(def secured-app
  (-> ring-app
    (friend/authenticate {:credential-fn (partial creds/bcrypt-credential-fn users)
                          :workflows [(workflows/interactive-form)]})
    ; ...required Ring middlewares ...
    ))

We have an unadorned (and unsecured) Ring application (ring-app, which can be any Ring handler), and then the usage of Friend's authenticate middleware. This is where all of the authentication work will be done, with the return value being a secured Ring application (secured-app), the requests to which are subject to the configuration provided to authenticate and the authorization contexts that are defined within ring-app (which we'll get to shortly).

(If you're newer to Clojure, you might not recognize the tokens prefixed with two colons [e.g. ::admin]. These are auto-namespaced keywords; in the example above, ::admin expands to :your.ring.app/admin.)

Authentication

There are two key abstractions employed during authentication: workflow and credential functions. The example above defines a single workflow — one supporting the POSTing of :username and :password parameters to (by default) /login — which will discover the specified :credential-fn and use it to validate submitted credentials. The bcrypt-credential-fn function verifies a submitted map of {:username "..." :password "..."} credentials against one loaded from another function based on the :username value; in this case, we're just looking up the username in a fixed Clojure map that has username, (bcrypted) password, and roles entries. If a submitted set of credentials matches those in the authoritative store, the latter are returned (sans :password) as an authentication map.

(Each workflow can have its own local configuration — including a credential function — that is used in preference to the configuration specified at the authenticate level.)

The authenticate middleware runs every incoming request through each of the workflows with which it is created. It further handles things like retaining authentication details in the user session (by default) and managing the redirection of users when they attempt to access protected resources without the requisite authentication or authorization (first to the start of an authentication workflow, e.g. GET of a /login URI, and then back to the originally-requested protected resource once the authentication workflow is completed).

(Note that Friend itself requires some core Ring middlewares: params, keyword-params and nested-params. Most workflows will additionally require session in order to support post-authentication redirection to previously-unauthorized resources, retention of tokens and nonces for workflows like OpenId and oAuth, etc. HTTP Basic is the only provided workflow that does not require session middleware.)

Workflows

Individual authentication methods (e.g., form-based auth, HTTP Basic, OpenID, oAuth, etc.) are implemented as workflows in Friend. A workflow is a regular Ring handler function, except that a workflow function can opt to return an authentication map instead of a Ring response if a request is authenticated. A diagram may help:

You can define any number of workflows in a :workflows kwarg to authenticate. Incoming requests are always run through the configured workflows prior to potentially being passed along to the secured Ring application.

If a workflow returns an authentication map, then the authenticate middleware will either:

  • carry on processing the request if the workflow allows for credentials to be provided in requests to any resource (i.e. HTTP Basic); control of this is entirely up to each workflow, and will be described later.
  • redirect the user agent to a secured resource that it was previously barred from accessing via Friend's authorization machinery

If a workflow returns a Ring response, then that response is sent back to the user agent straight away (after some bookkeeping by the authenticate middleware to preserve session states and such). This makes it possible for a workflow to control a "local" dataflow between itself, the user agent, and any necessary external authorities (e.g. by redirecting a user agent to an OpenId endpoint, performing token exchange in the case of oAuth, etc., eventually returning a complete authentication map that will allow the user agent to proceed on its desired vector).

Credential functions and authentication maps

Workflows use a credential function to verify the credentials provided to them via requests. Credential functions can be specified either as a :credential-fn option to cemerick.friend/authenticate, or often as an (overriding) :credential-fn option to individual workflow functions.

All credential functions take a single argument, a map containing the available credentials that additionally contains a :cemerick.friend/workflow slot identifying which workflow has produced the credential. For example, the default form-based authentication credential map looks like this:

{:username "...", :password "...", :cemerick.friend/workflow :form}

HTTP Basic credentials are much the same, but with a workflow value of :http-basic, etc. Different workflows may have significantly different credential maps (e.g. an OpenID workflow does not provide username and password, but rather a token returned by an OpenID provider along with potentially some number of "attributes" like the user's name, email address, default language, etc.), and unique credential verification requirements (again, contrast the simple username/password verification of form or HTTP Basic credentials and OpenId, which, in general, when presented with unknown credentials, should register the indicated identity rather than verifying it).

In summary, the contract of what exactly must be in the map provided to credential functions is entirely at the discretion of each workflow function, as is the semantics of the credential function.

If a map of credentials is verified by a credential function, it should return a authentication map that aggregates all authentication and authorization information available for the identified user. This map may contain many entries, depending upon the authentication information that is relevant for the workflow in question and the user data relevant to the application, but two entries are privileged:

  • :identity (required) corresponds with e.g. the username in a form or HTTP Basic authentication, an oAuth token, etc.; this value must be unique across all users within the application
  • :roles, an optional collection of values enumerating the roles for which the user is authorized, or a function returning the same.

If a map of credentials is found to be invalid, the credential function must return nil.

Authorization

As is, the example above doesn't do a lot: users can opt to be authenticated, but we've not described any kind of security policy, identified routes or functions or forms that require particular roles to access, and so on. This is where authorization mechanisms come into play.

While Friend has a single point of authentication — the authenticate middleware — it has many different options for restricting access to particular resources or code:

  • authenticated is a macro that requires that the current user must be authenticated
  • authorized? is a predicate that returns true only if the current user (as determined via the authentication map returned by a workflow) possesses the specified roles. You'll usually want to use one of the higher-level facilities (keep reading), but authorized? may come in handy if access to a certain resource or operation cannot be specified declaratively.

The rest of the authorization utilities use authorized? to determine whether a user may gain access to whatever the utility is protecting:

  • authorize is a macro that guards any body of code from being executed within a thread associated with a user that is not authorized?
  • wrap-authorize is a Ring middleware that only allows requests to pass through to the wrapped handler if their associated user is authorized?
  • authorize-hook is a function intended to be used with the Robert Hooke library that allows you to place authorization guards around functions defined in code you don't control.

Here's an extension of the example above that adds some actual routes (using Compojure) and handler that require authentication:

(use '[compojure.core :as compojure :only (GET ANY defroutes)])

(defroutes user-routes
  (GET "/account" request (page-bodies (:uri request)))
  (GET "/private-page" request (page-bodies (:uri request))))

(defroutes ring-app
  ;; requires user role
  (compojure/context "/user" request
    (friend/wrap-authorize user-routes #{::user}))

  ;; requires admin role
  (GET "/admin" request (friend/authorize #{::admin}
                          #_any-code-requiring-admin-authorization
                          "Admin page."))

  ;; anonymous
  (GET "/" request "Landing page.")
  (GET "/login" request "Login page.")
  (friend/logout (ANY "/logout" request (ring.util.response/redirect "/"))))

This should be easy to grok, but some highlights:

  • Authorization checks generally should happen after routing. This is usually easily accomplished by segregating handlers as you might do so anyway, and then using something like Compojure's context utility to wire them up into a common URI segment.
  • Alternatively, you can use authorize to put authorization guards around any code, anywhere.
  • The logout middleware can be applied to any Ring handler, and will remove all authentication information from the session assuming a non-nil response from the wrapped handler.

Note that, so far, all of the authorization checks will be completely "strict", e.g. the admin user won't have access to /user because it requires the ::user role. This is where hierarchies are unreasonably helpful.

Hierarchical roles (/ht derive, isa?, et al.)

The foundational authorized? predicate uses isa? to check if any of the current user's roles match one of those specified. This means that you can take advantage of Clojure's hierarchies via derive to establish relationships between roles. e.g., this is all that is required to give a user with the ::admin role all of the privileges of a user with the ::user role:

(derive ::admin ::user)

Of course, you are free to construct your role hierarchy(ies) however you like, to suit your application and your security requirements.

Nginx configuration

If you are using Nginx to, e.g, terminate SSL, set the appropriate headers so that the Clojure backend can generate the correct return-to URLs for the openid and similar workflows:

upstream jetty_upstream {
  ip_hash;
  server 127.0.0.1:8080;
  keepalive 64;
}

server {
  listen 443 ssl;
  #...SSL termination config, &c.
  
  location / {
    proxy_set_header host              $host;
    proxy_set_header x-forwarded-for   $remote_addr;
    proxy_set_header x-forwarded-host  $host;
    proxy_set_header x-forwarded-proto $scheme;
    proxy_set_header x-forwarded-port  $server_port;
    proxy_pass http://jetty_upstream;
  }
}

TODO

  • run-as/sudo/multi-user login
  • alternative hashing methods and salting strategies
    • good to encourage bcrypt, but existing apps have tons of sha-X, md5, etc passwords
  • remember-me?
  • fine-grained authorization (viz. ACLs, etc)
    • maybe something compelling can fall out of existing treatment of roles?
  • interop
    • recognize / provide access to servlet principal
    • spring-security
  • make :cemerick.friend/workflow metadata
  • documentation
    • authentication retention
    • authentication map metadata:
      • :type
      • ::friend/workflow
      • ::friend/redirect-on-auth?
      • ::friend/ensure-session

Need Help?

Ping cemerick on freenode irc or twitter if you have questions or would like to contribute patches.

License

Copyright ©2012-2013 Chas Emerick and other contributors.

Distributed under the Eclipse Public License, the same as Clojure. Please see the epl-v10.html file at the top level of this repo.

friend's People

Contributors

abp avatar anarsen avatar bonkydog avatar boreplusplus avatar cemerick avatar cldwalker avatar deliminator avatar flyingmachine avatar hardbap avatar jeluard avatar jkk avatar jszakmeister avatar kendru avatar kenrestivo avatar kierendavies avatar kolov avatar lynaghk avatar martintrojer avatar michaelblume avatar michaelklishin avatar otfrom avatar petrglad avatar rbxbx avatar tavisrudd avatar timothypratley avatar uros-r avatar woggle23 avatar yayitswei avatar ykomatsu avatar zerokarmaleft avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

friend's Issues

Support openid stateless mode

As mentioned here

https://groups.google.com/forum/?fromgroups=#!topic/openid4java/TEYtDLz-bPw

We can use openid4java in stateless mode if we replace InMemoryConsumerAssociationStore and InMemoryNonceVerifier with the Jdbc* variants. This is necessary when running a stateless webapp (state is in the database), for example on Heroku.

The discovery-cache used to store the provider-info will be useless in stateless mode - the provider-info will be stored and time out after max-nonce-age.

ConsumerManager.verify() will still work with provider-info equal nil - the provider-info will be re-fetched (additional network request).

I've tested this with my own implementation for ConsumerAssociationStore and MemoryNonceVerifier (didn't want to pull in the spring dependencies) and it seems to work without problems.

proposal: get user's roles based on current session

Hey Chas,

I'm trying to figure out how to write an app (paddleguru.com) that defines roles based on the particular resource being requested. I might be the admin of race A, but not race B.

I think the easiest way to make this work would be to allow :roles to be a function of the request. Alternatively, with the current code, I can bind the request dynamically and access it from inside the no-arg function.

Do you have a suggestion for a different way to tackle this case?

http-basic without redirect

Under the following circumstances:

  • Friend release 0.1.2
  • Using the http-basic workflow
  • With :allow-anon? true
  • The client requests a route that requires authorization
    (as by wrap-authorize)
  • The client does not supply any authentication headers

Friend will redirect to the :login-url instead of returning a 403 error.

Emails as Usernames

Does friend allow usernames to be emails? Whenever I assign an email to be a username i.e.

(def users {"ari" {:username "[email protected]"
                    :password (creds/hash-bcrypt "admin_password")
                    :roles #{::admin}}
            "jon" {:username "[email protected]"
                    :password (creds/hash-bcrypt "user_password")
                    :roles #{::user}}})

the authentication check, in spite of correct credentials, always fails.

Doc: Namespaced keywords

Please mention in the documentation that the role keywords have to be qualified if used in other namespace. A newcomer might not know namespaced keywords, :: is impossible to look up, so such a hint would be very valuable.

:roles for authorization are static from authentication time

From my Using friend in clojars writeup:

While exploring friend, a downside was noted with the authorization. When a user is authenticated, the :roles are added into the session. If the user gets a new role it will not take effect without a login/logout. Using derive to produce a in memory role hierarchy can work with this, as suggested in the friend readme.

This seemed like a bad idea for clojars. The direct mapping of groups to roles would require trying to keep the roles in sync with the database, and reinitialized on a restart. Instead clojars uses its own mechanism for authorization wrapping (friend/throw-unauthorized friend/*identity*).

It would be nice to have a mechanism to update a user's :roles, or allow querying them at authorization time.

Guice dependency can't be resolved

When I try to build a project that depends on friend, it fails:

[ERROR] Failed to execute goal on project foo: … Failed to read artifact descriptor for com.google.code.guice:guice:jar:2.0: Could not transfer artifact com.google.code.guice:guice:pom:2.0 from/to guice (http://guice-maven.googlecode.com/svn/trunk): Access denied to: http://guice-maven.googlecode.com/svn/trunk/com/google/code/guice/guice/2.0/guice-2.0.pom -> [Help 1]

Workflow with no redirection prior to authentication

I have a hard time trying to figure out how to get a workflow that does not redirect to the login page before authentication.

On a private resource I would like a pass only if the user has been authenticated with a previous request (so when there is a valid session cookie) and a 403 otherwise.

Login and access are decoupled. You login then you access (separately) or you don't access.

I have been trying to dig into the source without much success so far ;-)

Thanks for your help

util use of core incubator

I was attempting to use workflows/http-basic which uses friend/util which uses clojure/core/incubator. This appears to not be available in org.clojure/clojure "1.2.1".

Exception in thread "main" java.io.FileNotFoundException: Could not locate clojure/core/incubator__init.class or clojure/core/incubator.clj on classpath: (util.clj:1)

Can not require cemerick.friend

This code

(ns cljdb.server
  (:require [cemerick.friend :as friend]))

fails with error

CompilerException java.lang.RuntimeException: No such var: response/response?, compiling:(cemerick/friend.clj:132) 

when loaded in Eclipse's counterclockwise REPL.
The issue can be reproduced with Leiningen project.clj

(defproject cljdb "0.1.0-SNAPSHOT"
  :dependencies [
                 [org.clojure/clojure "1.4.0"]
                 [com.cemerick/friend "0.1.2"]])

Super-pom 2.0 not found

I figured I'd take a look at what'd be involved in extending this (oauth2, perhaps), but ran into this right away:

1 required artifact is missing.

for artifact: 
  org.apache.maven:super-pom:pom:2.0

from the specified remote repositories:
  central (http://repo1.maven.org/maven2),
  clojars (http://clojars.org/repo/)

I looked around and couldn't find super-pom 2.0, just other versions. I wasn't clear where the dependency on super-pom 2.0 was coming from; it doesn't seem to be in the project.clj. I find it odd that the pom for org.openid4java:openid4java-consumer:jar:0.9.6 would depend on an artifact that isn't in central, but apparently it does?

This is with lein 1.7.1, BTW.

provide option for setting status on redirect from requires-scheme

There is a TODO here:
https://github.com/cemerick/friend/blob/master/src/cemerick/friend.clj#L32

After researching this issue a little bit, I think the general opinion is that 301 (permanent redirect) plays better with some search engines.

One of the reasons I'm concerned about this is that google is currently indexing my http://... url rather than my https://... url. I think changing to 301 may solve this issue.

What do you think?

http://moz.com/learn/seo/redirection
http://www.bigoakinc.com/blog/when-to-use-a-301-vs-302-redirect/
http://blog.mastykarz.nl/sharepoint-2007-redirect-solved-using-301-instead-of-302-redirects/
http://www.mattcutts.com/blog/seo-advice-discussing-302-redirects/

Session is reset on successful login

When I'm logging in with openid, I'm experiencing the effect that the return to the landing uri will overwrite all previous session state.

I debugged it and in friend.clj, in authentiate*, the following is the cause for my problem. As can be seen, the session is reset:

                (if new-auth?
                  (-> (or (redirect-new-auth workflow-result request)
                          (handler request))
                    ring-response
                    (assoc-in [:session ::identity] auth))
                  (handler request))

The following preserves the session on login and fixes my problem:

                (if new-auth?
                  (-> (or (redirect-new-auth workflow-result request)
                          (handler request))
                    ring-response
                    (assoc :session (-> request :session))
                    (assoc-in [:session ::identity] auth))
                  (handler request))
                (catch authorization-failur

Is this a bug? If not, is there a way to preserve the session on login?

http-basic authentication challenge when no credentials are provided

workflows/http-basic does not currently present a challenge response ("HTTP/1.1 401 Authorization Required" + "Www-Authenticate: Basic realm=<realm>") when no credentials are provided.

Many client APIs consume http basic auth protected resources by requesting without credentials first and checking for a challenge, then providing credentials. workflows/http-basic should probably call workflows/http-basic-deny in the event no credentials are supplied (in addition to incorrect credentials) in order to give such APIs the opportunity to provide credentials.

OAuth2 workflow

I've got a preliminary "proof-of-concept" up and running, I've tested it manually with app.net and Facebook, and it works.

https://github.com/ddellacosta/friend-oauth2

If you think it makes sense, after some more tuning, perhaps it could be included in Friend. Or it can be kept as a separate library. I haven't set it up to work with clojars yet, but I intend to if you think it would be suited better as a separate library.

In any case, when you get the chance please take a look and let me know what you think. It's still pretty rough, but I think I'm moving in the right direction.

I should add, I'm relatively new to Clojure so forgive my naiveté...and feel free to make style suggestions if you are so inclined.

By the way, thanks again to you and xeqi for your help on IRC the other day!

programmatically obtaining openid redirect url

The openid workflow expects a post to (by default) /openid with the identifier to generate a redirection URL to - should that be exposed as a function that can be called directly? (at least I think it is buried in the openid/workflow

(feel free to point out I am Doing It Wrong).

Brilliant library !

current-authentication returns nil when using shoreleave RPC

Hi,

I'm currently using Friend as well as Shoreleave RPC.
When I use normal page render calls and I do (cemerick.friend/current-authentication) I get what I expect i.e. the current logged in user details.
But when my front end CLJS app makes an RPC call using a Shoreleave http-rpc call the server side returns nil for (cemerick.friend/current-authentication).

Is there a way to get the current logged in users details for a shoreleave http-rpc call?

Any ideas?

Thanks.

:login-uri seems to be required when using form based auth

:login-uri looks like it should default to "/login" however the login process does not seem to work unless it is specified. A simple app will look as below and commenting out the login-uri parameter causes it to stop working.

(def app
(-> ring-app
(friend/authenticate
{:credential-fn (partial creds/bcrypt-credential-fn users)
:unauthorized-redirect-uri "/login"
;; commenting out following line does not work
:login-uri "/login"
:workflows [(workflows/interactive-form)]})
handler/site))

request for upgraded dependency com.google.inject/guice, conflicts with jclouds

It appears the [com.google.inject/guice "2.0"] dependency takes precedency over "3.0" which jclouds is looking for. Would you be willing to release a version with updated dependencies?

;; project.clj

(defproject xyz "0.1.0-SNAPSHOT"
  :plugins [[lein-deps-tree "0.1.2"]]
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/core.incubator "0.1.3"]
                 [org.jclouds/jclouds-allblobstore "1.6.0"]
                 ;[com.cemerick/friend "0.1.5"]
                 [org.clojure/tools.logging "0.2.6"]])


;; with [com.cemerick/friend "0.1.5"]

% lein deps-tree
[clojure-complete "0.2.3"]
[com.cemerick/friend "0.1.5"]
    [com.google.inject/guice "2.0"]
        [aopalliance "1.0"]
    [commons-codec "1.6"]
    [net.sourceforge.nekohtml/nekohtml "1.9.10"]
        [xerces/xercesImpl "2.8.1"]
            [xml-apis "1.3.03"]
    [org.apache.httpcomponents/httpclient "4.2.1"]
        [org.apache.httpcomponents/httpcore "4.2.1"]
    [org.clojure/core.cache "0.6.2"]
    [org.mindrot/jbcrypt "0.3m"]
    [org.openid4java/openid4java-nodeps "0.9.6" :exclusions [[com.google.code.guice/guice]]]
        [commons-logging "1.1.1"]
        [net.jcip/jcip-annotations "1.0"]
    [ring/ring-core "1.2.0-beta1"]
        [clj-time "0.4.4"]
            [joda-time "2.1"]
        [commons-fileupload "1.2.2"]
        [commons-io "2.4"]
        [javax.servlet/servlet-api "2.5"]
        [ring/ring-codec "1.0.0"]
    [robert/hooke "1.1.2"]
    [slingshot "0.10.2"]
[org.clojure/clojure "1.5.1"]
[org.clojure/core.incubator "0.1.3"]
[org.clojure/tools.logging "0.2.6"]
[org.clojure/tools.nrepl "0.2.3"]
[org.jclouds/jclouds-allblobstore "1.6.0"]
    [org.jclouds.api/atmos "1.6.0"]
    [org.jclouds.api/cloudfiles "1.6.0"]
    [org.jclouds.api/filesystem "1.6.0"]
    [org.jclouds.api/s3 "1.6.0"]
        [com.jamesmurty.utils/java-xmlbuilder "0.4"]
        [org.jclouds.api/sts "1.6.0"]
            [org.jclouds/jclouds-core "1.6.0"]
                [com.google.code.gson/gson "2.2.2"]
                [com.google.guava/guava "14.0.1"]
                [com.google.inject.extensions/guice-assistedinject "3.0"]
                [javax.annotation/jsr250-api "1.0"]
                [javax.inject "1"]
                [javax.ws.rs/jsr311-api "1.1.1"]
                [org.99soft.guice/rocoto "6.2"]
        [org.jclouds/jclouds-blobstore "1.6.0"]
    [org.jclouds.api/swift "1.6.0"]
        [org.jclouds.api/openstack-keystone "1.6.0"]
        [org.jclouds.common/openstack-common "1.6.0"]
    [org.jclouds.provider/aws-s3 "1.6.0"]
    [org.jclouds.provider/azureblob "1.6.0"]
        [org.jclouds.common/azure-common "1.6.0"]
    [org.jclouds.provider/cloudfiles-uk "1.6.0"]
    [org.jclouds.provider/cloudfiles-us "1.6.0"]
    [org.jclouds.provider/cloudonestorage "1.6.0"]
    [org.jclouds.provider/hpcloud-objectstorage "1.6.0"]
    [org.jclouds.provider/ninefold-storage "1.6.0"]

% lein repl
(use 'org.jclouds.blobstore2)
; nil
(System/setProperty "jclouds.filesystem.basedir" "/tmp/foo")
; nil
(def bs (blobstore "filesystem" "foo" "bar"))
; ClassNotFoundException com.google.inject.internal.util.$Preconditions  java.net.URLClassLoader$1.run (URLClassLoader.java:365)


;; without [com.cemerick/friend "0.1.5"]

% lein deps-tree
[clojure-complete "0.2.3"]
[org.clojure/clojure "1.5.1"]
[org.clojure/core.incubator "0.1.3"]
[org.clojure/tools.logging "0.2.6"]
[org.clojure/tools.nrepl "0.2.3"]
[org.jclouds/jclouds-allblobstore "1.6.0"]
    [org.jclouds.api/atmos "1.6.0"]
    [org.jclouds.api/cloudfiles "1.6.0"]
    [org.jclouds.api/filesystem "1.6.0"]
    [org.jclouds.api/s3 "1.6.0"]
        [com.jamesmurty.utils/java-xmlbuilder "0.4"]
        [org.jclouds.api/sts "1.6.0"]
            [org.jclouds/jclouds-core "1.6.0"]
                [com.google.code.gson/gson "2.2.2"]
                [com.google.guava/guava "14.0.1"]
                [com.google.inject.extensions/guice-assistedinject "3.0"]
                [com.google.inject/guice "3.0"]
                    [aopalliance "1.0"]
                [javax.annotation/jsr250-api "1.0"]
                [javax.inject "1"]
                [javax.ws.rs/jsr311-api "1.1.1"]
                [org.99soft.guice/rocoto "6.2"]
        [org.jclouds/jclouds-blobstore "1.6.0"]
    [org.jclouds.api/swift "1.6.0"]
        [org.jclouds.api/openstack-keystone "1.6.0"]
        [org.jclouds.common/openstack-common "1.6.0"]
    [org.jclouds.provider/aws-s3 "1.6.0"]
    [org.jclouds.provider/azureblob "1.6.0"]
        [org.jclouds.common/azure-common "1.6.0"]
    [org.jclouds.provider/cloudfiles-uk "1.6.0"]
    [org.jclouds.provider/cloudfiles-us "1.6.0"]
    [org.jclouds.provider/cloudonestorage "1.6.0"]
    [org.jclouds.provider/hpcloud-objectstorage "1.6.0"]
    [org.jclouds.provider/ninefold-storage "1.6.0"]

% lein repl
(use 'org.jclouds.blobstore2)
; nil
(System/setProperty "jclouds.filesystem.basedir" "/tmp/foo")
; nil
(def bs (blobstore "filesystem" "foo" "bar"))
; #'user/bs

http-basic chokes when no username is provided

If a user-agent provides a basic authenticator that does not contain a username (password-only), the http-basic workflow blows up with a "Malformed Authorization header for HTTP Basic authentication." error.

Desired behavior: it should be possible to authenticate a user using basic authentication even when no username has been supplied.

Default login-uri not valid in Location header

The default :login-uri is /login, which gets used in the "Location" header when generating redirects. Browsers may accept this, but it is not valid according to the HTTP spec, which requires an absolute URI.

Clients which expect an absolute URI in the "Location" header will fail in this case.

See this line in friend.clj

HOWTO: using friend with a ClojureScript app

Chas,

I'm looking into using friend to provide security for a ClojureScript clientside application that talks a compojure server through a single route with heavy use of prn and read-string (similar to how ibdknox's fetch works).
Do you have any pointers as to the best way to leverage friend in this scenario?
In particular, does the clientside cljs need to juggle authtokens or some such, or is the browser smart enough to do all of that with headers?

extra check option for wrap-authorize?

hi, I have the user resource, in it the update user endpoint (PUT /api/user) can be accessed only by two kind of users:

  • the user which is updating its own user entity (for example changing a field or updating the password)
  • someone with a role that allows to update other users (:user-admin or :admin)

the second case is covered by wrap-authorize, but the first isn't, I would like to wrap my handler when I'm building the app and then have it run the check to see if the authenticated user is trying to update its own entity and if it fails check the roles (or the other way around)

is there any other way in which this can be done with the existing code?

friend authenticate uses ->> rather than ->

Other ring middleware has only one parameter or has the handler/app in the 1st position.

Here are some examples:

ring.middleware.multipart-params
ring.middleware.file
ring.middleware.file-info
ring.middleware.multipart-params
ring.middleware.nested-params

Is there any particular reason for having it the other way around?

always add values to :session

Friend looks like it only adds identity information for a new-auth?. This means if another part of the stack adds data to the :session for a request that passes authorization, the identity information will be forgotten.

Talking to @weavejester on irc it sounds like friend needs to be responsible for copying the information it desires to be saved from the request's :session to keep it. Perhaps https://github.com/mmcgrana/ring/blob/master/ring-core/src/ring/middleware/flash.clj could be helpful as a template.

Last URI redirect not working when favicon.ico is missing

It appears that when a linked file (such as favicon.ico) is requested resulting in a 404, friend uses that resource as the last uri to redirect to after authetication. I ran into this problem on Chrome without fail, but after the first 404 Firefox ignored it.

"Exception in thread "main" java.lang.RuntimeException: No such var: response/response?, compiling:(cemerick/friend.clj:133)"

I ran into the previously described issue whereby the order of the friend dependency in the Leiningen project.clj file seems to conflict with compojure (and possibly other libraries).

I tried using an exclusion but that just seems to produe this new error.

My project.clj:

(defproject hello-heroku-clojure-world "1.0.0-SNAPSHOT"
  :description "Sandbox Clojure web app intended to run on Heroku"
  :url "http://hello-heroku-clojure-world.herokuapp.com"
  :license {:name "WTFPL"
            :url "http://www.wtfpl.net/"}
  :dependencies [ [org.clojure/clojure "1.4.0"]          
                  [org.clojure/java.jdbc "0.2.3"]

                  [clj-time "0.5.0"]
                  [com.cemerick/drawbridge "0.0.6"]
                  [com.cemerick/friend "0.2.0"]
                  [compojure "1.1.1"]
                  [hiccup "1.0.3"]
                  [environ "0.2.1"]
                  [postgresql "9.1-901.jdbc4"]
                  [ring/ring-jetty-adapter "1.1.0"]
                  [ring/ring-devel "1.1.0"]
                  [ring-basic-authentication "1.0.1"]]
  :min-lein-version "2.0.0"
  :plugins [[environ/environ.lein "0.2.1"]]
  :hooks [environ.leiningen.hooks]
  :profiles {:production {:env {:production true}}}
  :main hello-heroku-clojure-world.web)

The output of lein deps :tree:

WARNING!!! possible confusing dependencies found:
[org.clojure/clojure "1.4.0"]
 overrides
[clj-time "0.5.0"] -> [org.clojure/clojure "1.5.1"]

[com.cemerick/drawbridge "0.0.6"] -> [ring/ring-core "1.0.2"]
 overrides
[com.cemerick/friend "0.2.0"] -> [ring/ring-core "1.2.0"]
 and
[compojure "1.1.1"] -> [ring/ring-core "1.1.1"]
 and
[ring/ring-jetty-adapter "1.1.0"] -> [ring/ring-core "1.1.0"]
 and
[ring/ring-devel "1.1.0"] -> [ring/ring-core "1.1.0"]
 and
[ring/ring-jetty-adapter "1.1.0"] -> [ring/ring-servlet "1.1.0"] -> [ring/ring-core "1.1.0"]

[com.cemerick/drawbridge "0.0.6"] -> [cheshire "3.0.0"]
 overrides
[com.cemerick/drawbridge "0.0.6"] -> [clj-http "0.3.6"] -> [cheshire "3.1.0"]

 [clj-time "0.5.0"]
   [joda-time "2.2"]
 [clojure-complete "0.2.3" :exclusions [[org.clojure/clojure]]]
 [com.cemerick/drawbridge "0.0.6"]
   [cheshire "3.0.0"]
     [org.codehaus.jackson/jackson-core-asl "1.9.5"]
     [org.codehaus.jackson/jackson-smile "1.9.5"]
   [clj-http "0.3.6"]
     [org.apache.httpcomponents/httpmime "4.1.2"]
   [ring/ring-core "1.0.2"]
     [commons-fileupload "1.2.1"]
     [commons-io "1.4"]
     [javax.servlet/servlet-api "2.5"]
 [com.cemerick/friend "0.2.0"]
   [com.google.inject/guice "2.0"]
     [aopalliance "1.0"]
   [commons-codec "1.6"]
   [net.sourceforge.nekohtml/nekohtml "1.9.10"]
     [xerces/xercesImpl "2.8.1"]
       [xml-apis "1.3.03"]
   [org.apache.httpcomponents/httpclient "4.2.1"]
     [org.apache.httpcomponents/httpcore "4.2.1"]
   [org.clojure/core.cache "0.6.2"]
   [org.clojure/core.incubator "0.1.1"]
   [org.mindrot/jbcrypt "0.3m"]
   [org.openid4java/openid4java-nodeps "0.9.6" :exclusions [[com.google.code.guice/guice]]]
     [commons-logging "1.1.1"]
     [net.jcip/jcip-annotations "1.0"]
   [robert/hooke "1.1.2"]
   [slingshot "0.10.2"]
 [compojure "1.1.1"]
   [clout "1.0.1"]
   [org.clojure/tools.macro "0.1.0"]
 [environ "0.2.1"]
   [environ/environ.core "0.2.1"]
 [hiccup "1.0.3"]
 [org.clojure/clojure "1.4.0"]
 [org.clojure/java.jdbc "0.2.3"]
 [org.clojure/tools.nrepl "0.2.3" :exclusions [[org.clojure/clojure]]]
 [postgresql "9.1-901.jdbc4"]
 [ring-basic-authentication "1.0.1"]
   [org.clojure/data.codec "0.1.0"]
 [ring/ring-devel "1.1.0"]
   [clj-stacktrace "0.2.4"]
   [ns-tracker "0.1.1"]
     [org.clojure/tools.namespace "0.1.0"]
       [org.clojure/java.classpath "0.1.0"]
 [ring/ring-jetty-adapter "1.1.0"]
   [org.eclipse.jetty/jetty-server "7.6.1.v20120215"]
     [org.eclipse.jetty.orbit/javax.servlet "2.5.0.v201103041518"]
     [org.eclipse.jetty/jetty-continuation "7.6.1.v20120215"]
     [org.eclipse.jetty/jetty-http "7.6.1.v20120215"]
       [org.eclipse.jetty/jetty-io "7.6.1.v20120215"]
         [org.eclipse.jetty/jetty-util "7.6.1.v20120215"]
   [ring/ring-servlet "1.1.0"]

Friend requires old version of ring

Friend specifies an old version of ring that does not have the head middleware needed by compojure.route causing:

"Caused by: java.io.FileNotFoundException: Could not locate ring/middleware/head__init.class or ring/middleware/head.clj on classpath:"

  • Sorry if this is not really an issue I'm not yet quite sure how to resolve all these various dependencies. Thanks.

Question: Google Apps support?

Quick question: Does friend support OpenID authentication to Google Apps? I know it supports authentication for general Google accounts, but does it work with Google's Apps discovery process?

Thanks!

Working with Monger

I am really sorry for taking your time but I'm a new Clojure user and I cant understand everything.
My question is how to keep #{:cemerick.friend-demo.users/user} from becoming "user" while adding to database using Monger?
I'm really sorry again and I'm not sure if I should be asking this to a Monger user.

Keep up the good work!
Thanks and apologies :/

Example is very misleading

It looks like the default behavior in authenticate* is allow-anon? true. This makes the example extremely misleading, since the supposedly secured app allows any request by default, and you must dig into the source to discover why this is so.

Honestly I don't know why this is an option vs. not using friend for things that should have anonymous access, but this is a rotten thing to omit in an example.

be able to start openid flow programmatically and omit landing page

https://groups.google.com/forum/#!topic/clojure/g35bhIpQ3ME

In your first answer to that post you mention starting the openid workflow automatically, which would be confusing for normal users, but not for e.g. company internal applications which may not need extra landing pages explaining what the application does (as that's explained either extra or in the application itself).

So the authentication workflow would be: /index -> redirect to /openid -> authentication via e.g. Google -> redirect to /index

https://github.com/cemerick/friend/blob/master/src/cemerick/friend/openid.clj#L108

The way I see it, the code around line 108 would need to handle GET requests and use an identifier existing in the openid-config map.

Currently broken with lein-ring/compojure

I'm getting the following errors when using friend with compojure:

Exception in thread "main" java.io.FileNotFoundException: Could not locate ring/util/request__init.class or ring/util/request.clj on classpath: 
        at clojure.lang.RT.load(RT.java:443)
        at clojure.lang.RT.load(RT.java:411)
        at clojure.core$load$fn__5018.invoke(core.clj:5530)
        at clojure.core$load.doInvoke(core.clj:5529)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invoke(core.clj:5336)
        at clojure.core$load_lib$fn__4967.invoke(core.clj:5375)
        at clojure.core$load_lib.doInvoke(core.clj:5374)
        at clojure.lang.RestFn.applyTo(RestFn.java:142)
        at clojure.core$apply.invoke(core.clj:619)
        at clojure.core$load_libs.doInvoke(core.clj:5413)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.core$apply.invoke(core.clj:619)
        at clojure.core$require.doInvoke(core.clj:5496)
        at clojure.lang.RestFn.invoke(RestFn.java:436)
        at cemerick.friend.workflows$eval2018$loading__4910__auto____2019.invoke(workflows.clj:1)
        at cemerick.friend.workflows$eval2018.invoke(workflows.clj:1)
        at clojure.lang.Compiler.eval(Compiler.java:6619)
        at clojure.lang.Compiler.eval(Compiler.java:6608)
        at clojure.lang.Compiler.load(Compiler.java:7064)
        at clojure.lang.RT.loadResourceScript(RT.java:370)
        at clojure.lang.RT.loadResourceScript(RT.java:361)
        at clojure.lang.RT.load(RT.java:440)
        at clojure.lang.RT.load(RT.java:411)
        at clojure.core$load$fn__5018.invoke(core.clj:5530)
        at clojure.core$load.doInvoke(core.clj:5529)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invoke(core.clj:5336)
        at clojure.core$load_lib$fn__4967.invoke(core.clj:5375)
        at clojure.core$load_lib.doInvoke(core.clj:5374)
        at clojure.lang.RestFn.applyTo(RestFn.java:142)
        at clojure.core$apply.invoke(core.clj:619)
        at clojure.core$load_libs.doInvoke(core.clj:5417)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.core$apply.invoke(core.clj:619)
        at clojure.core$require.doInvoke(core.clj:5496)
        at clojure.lang.RestFn.invoke(RestFn.java:457)
        at lyrionweb.core$eval1679$loading__4910__auto____1680.invoke(core.clj:1)
        at lyrionweb.core$eval1679.invoke(core.clj:1)
        at clojure.lang.Compiler.eval(Compiler.java:6619)
        at clojure.lang.Compiler.eval(Compiler.java:6608)
        at clojure.lang.Compiler.load(Compiler.java:7064)
        at clojure.lang.RT.loadResourceScript(RT.java:370)
        at clojure.lang.RT.loadResourceScript(RT.java:361)
        at clojure.lang.RT.load(RT.java:440)
        at clojure.lang.RT.load(RT.java:411)
        at clojure.core$load$fn__5018.invoke(core.clj:5530)
        at clojure.core$load.doInvoke(core.clj:5529)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invoke(core.clj:5336)
        at clojure.core$load_lib$fn__4967.invoke(core.clj:5375)
        at clojure.core$load_lib.doInvoke(core.clj:5374)
        at clojure.lang.RestFn.applyTo(RestFn.java:142)
        at clojure.core$apply.invoke(core.clj:619)
        at clojure.core$load_libs.doInvoke(core.clj:5413)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.core$apply.invoke(core.clj:619)
        at clojure.core$require.doInvoke(core.clj:5496)
        at clojure.lang.RestFn.invoke(RestFn.java:421)
        at user$eval3.invoke(NO_SOURCE_FILE:1)
        at clojure.lang.Compiler.eval(Compiler.java:6619)
        at clojure.lang.Compiler.eval(Compiler.java:6608)
        at clojure.lang.Compiler.eval(Compiler.java:6582)
        at clojure.core$eval.invoke(core.clj:2852)
        at clojure.main$eval_opt.invoke(main.clj:308)
        at clojure.main$initialize.invoke(main.clj:327)
        at clojure.main$null_opt.invoke(main.clj:362)
        at clojure.main$main.doInvoke(main.clj:440)
        at clojure.lang.RestFn.invoke(RestFn.java:421)
        at clojure.lang.Var.invoke(Var.java:419)
        at clojure.lang.AFn.applyToHelper(AFn.java:163)
        at clojure.lang.Var.applyTo(Var.java:532)
        at clojure.main.main(main.java:37)
Subprocess failed

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.