├── .gitignore
├── MIT-LICENSE
├── README.md
├── app.yml
├── build.gradle
├── gradle.properties
├── pom.xml
└── src
└── main
├── java
└── uk
│ └── co
│ └── froot
│ └── demo
│ └── openid
│ ├── AppConfiguration.java
│ ├── OpenIDDemoService.java
│ ├── auth
│ ├── annotation
│ │ └── RestrictedTo.java
│ └── openid
│ │ ├── OpenIDAuthenticator.java
│ │ ├── OpenIDCredentials.java
│ │ ├── OpenIDRestrictedToInjectable.java
│ │ └── OpenIDRestrictedToProvider.java
│ ├── core
│ ├── InMemoryOpenIDCache.java
│ └── InMemoryUserCache.java
│ ├── health
│ └── OpenIdDemoHealthCheck.java
│ ├── model
│ ├── BaseModel.java
│ ├── ModelBuilder.java
│ ├── openid
│ │ ├── DiscoveryInformationMemento.java
│ │ └── TokenMemento.java
│ └── security
│ │ ├── Authority.java
│ │ └── User.java
│ ├── resources
│ ├── BaseResource.java
│ ├── PrivateInfoResource.java
│ ├── PublicErrorResource.java
│ ├── PublicHomeResource.java
│ ├── PublicOAuthResource.java
│ ├── PublicOpenIDResource.java
│ └── RuntimeExceptionMapper.java
│ └── views
│ └── PublicFreemarkerView.java
└── resources
├── assets
├── favicon.ico
├── images
│ ├── error.jpg
│ ├── signin-facebook.png
│ ├── signin-google.png
│ ├── signin-googleplus.png
│ ├── signin-linkedin.png
│ ├── signin-openid.png
│ ├── signin-twitter.png
│ └── signin-yahoo.png
└── jquery
│ └── plugins
│ └── openid-selector
│ ├── css
│ ├── openid-shadow.css
│ └── openid.css
│ ├── images.large
│ ├── aol.gif
│ ├── facebook.gif
│ ├── google.gif
│ ├── mailru.gif
│ ├── myopenid.gif
│ ├── openid.gif
│ ├── rambler.gif
│ ├── verisign.gif
│ ├── vkontakte.gif
│ ├── yahoo.gif
│ └── yandex.gif
│ ├── images.small
│ ├── aol.ico
│ ├── aol.ico.gif
│ ├── aol.ico.png
│ ├── blogger.ico
│ ├── blogger.ico.gif
│ ├── blogger.ico.png
│ ├── claimid.ico
│ ├── claimid.ico.gif
│ ├── claimid.ico.png
│ ├── clickpass.ico
│ ├── clickpass.ico.gif
│ ├── clickpass.ico.png
│ ├── facebook.ico
│ ├── facebook.ico.gif
│ ├── facebook.ico.png
│ ├── flickr.ico
│ ├── flickr.ico.gif
│ ├── flickr.ico.png
│ ├── google.ico
│ ├── google.ico.gif
│ ├── google.ico.png
│ ├── google_profile.ico
│ ├── google_profile.ico.gif
│ ├── google_profile.ico.png
│ ├── launchpad.ico
│ ├── launchpad.ico.gif
│ ├── launchpad.ico.png
│ ├── linkedin.ico
│ ├── linkedin.ico.gif
│ ├── linkedin.ico.png
│ ├── livejournal.ico
│ ├── livejournal.ico.gif
│ ├── livejournal.ico.png
│ ├── mailru.ico
│ ├── mailru.ico.gif
│ ├── mailru.ico.png
│ ├── myopenid.ico
│ ├── myopenid.ico.gif
│ ├── myopenid.ico.png
│ ├── openid.ico
│ ├── openid.ico.gif
│ ├── openid.ico.png
│ ├── rambler.ico
│ ├── rambler.ico.gif
│ ├── rambler.ico.png
│ ├── technorati.ico
│ ├── technorati.ico.gif
│ ├── technorati.ico.png
│ ├── twitter.ico
│ ├── twitter.ico.gif
│ ├── twitter.ico.png
│ ├── verisign.ico
│ ├── verisign.ico.gif
│ ├── verisign.ico.png
│ ├── vidoop.ico
│ ├── vidoop.ico.gif
│ ├── vidoop.ico.png
│ ├── vkontakte.ico
│ ├── vkontakte.ico.gif
│ ├── vkontakte.ico.png
│ ├── winliveid.ico
│ ├── winliveid.ico.gif
│ ├── winliveid.ico.png
│ ├── wordpress.ico
│ ├── wordpress.ico.gif
│ ├── wordpress.ico.png
│ ├── yahoo.ico
│ ├── yahoo.ico.gif
│ ├── yahoo.ico.png
│ ├── yandex.ico
│ ├── yandex.ico.gif
│ └── yandex.ico.png
│ ├── images
│ ├── openid-inputicon.gif
│ ├── openid-providers-en.png
│ └── openid-providers-ru.png
│ └── js
│ ├── openid-de.js
│ ├── openid-en.js
│ ├── openid-jp.js
│ ├── openid-jquery.js
│ ├── openid-ru.js
│ └── openid-uk.js
├── banner.txt
└── views
├── ftl
├── common
│ ├── home.ftl
│ ├── login.ftl
│ └── markdown.ftl
├── error
│ ├── 401.ftl
│ ├── 404.ftl
│ └── 500.ftl
├── includes
│ └── common
│ │ ├── cdn-scripts.ftl
│ │ ├── footer.ftl
│ │ ├── head.ftl
│ │ ├── header.ftl
│ │ ├── left-sidebar.ftl
│ │ └── right-sidebar.ftl
└── private
│ ├── admin.ftl
│ └── home.ftl
└── markdown
└── demo-all-elements.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath
2 | .project
3 | .settings/
4 | .idea/
5 | .gradle/
6 | *.iml
7 | *.iws
8 | .DS_Store
9 | log/
10 | target/
11 | /build
12 | /bin
13 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011-2012 Gary Rowe, http://gary-rowe.com/
2 |
3 | This software consists of voluntary contributions made by many
4 | individuals (see AUTHORS.txt) For the exact contribution history,
5 | see the revision history and logs, available at
6 | https://github.com/gary-rowe/MultiBitMerchant
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining
9 | a copy of this software and associated documentation files (the
10 | "Software"), to deal in the Software without restriction, including
11 | without limitation the rights to use, copy, modify, merge, publish,
12 | distribute, sublicense, and/or sell copies of the Software, and to
13 | permit persons to whom the Software is furnished to do so, subject to
14 | the following conditions:
15 |
16 | The above copyright notice and this permission notice shall be
17 | included in all copies or substantial portions of the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OAuth Dropwizard Demo
2 |
3 | This project is based on [Gary Rowe's OpenID Dropwizard sample](https://github.com/gary-rowe/DropwizardOpenID),
4 | but integrates OAuth support for authentication instead, because some
5 | applications need to be able to post to a user's timeline or access photos,
6 | etc. in their social network. OpenID is only for authentication, not
7 | authorization. OAuth can be used for authentication (as long as
8 | you request email address access) and authorization to use the
9 | social network.
10 |
11 | Note that you can also login using OpenID. OAuth as implemented in this
12 | is an alternative login method, but one that also gives you access to
13 | feeds/contacts/albums.
14 |
15 |
16 | ## OAuth Libraries
17 |
18 | Unfortunately, there's no single Java library that does everything. Every
19 | library seems to have a weakness:
20 |
21 | * [Scribe](https://github.com/fernandezpablo85/scribe-java) - only covers
22 | OAuth URL generation, access token creation
23 | from the request token and signing subsequent requests, but doesn't
24 | wrap any of the APIs (you have to call the REST endpoints yourself)
25 | * [Spring Social](http://www.springsource.org/spring-social) - provides
26 | OAuth URL generation, a servlet filter to handle the return tokens,
27 | controllers for Spring MVC, and wrappers for the social network APIs,
28 | but doesn't include Google+ APIs (though there is a
29 | [3rd party plugin](https://github.com/GabiAxel/spring-social-google) for Google+).
30 | * [SocialAuth](http://code.google.com/p/socialauth) - similar
31 | to Spring Social in that it provides OAuth handling, a servlet filter to
32 | handle the returned token, integration examples for Seam and JBoss CDI/JSF
33 | and Struts and Spring MVC, and plugins to wrapper the social network feed
34 | APIs (only Facebook, Twitter, and LinkedIn, but not Google+ oddly enough).
35 | * [Google's OAuth Library](http://code.google.com/p/google-oauth-java-client/)
36 | and [API Client Library](http://code.google.com/p/google-api-java-client/)
37 | can be used for access to Google's social network to fill in any gaps
38 | the other libraries are missing.
39 |
40 | Scribe is the most flexible, but also the most basic.
41 | SocialAuth is a middle ground where you can write plugins for accessing
42 | parts of each social network.
43 | Spring Social is the most comprehensive, but also the most complicated
44 | to hook in and is best used with other parts of the Spring Framework.
45 |
46 | SocialAuth seemed the best middle ground, so that was used for
47 | the OAuth support for this application.
48 |
49 | ## Getting started
50 |
51 | From an IDE, just run the `OpenIDDemoService.main()` with a runtime
52 | configuration that passes application parameters of `server app.yml`.
53 |
54 | From the console, just build with Maven and run it:
55 | ```
56 | mvn clean install
57 | java -jar target/dropwizard-openid-1.1.0.jar server app.yml
58 | ```
59 |
60 | From the console, you can also use Gradle to build it into a single jar and run
61 | ```
62 | gradle runShadow
63 | ```
64 |
65 | ## Proxy settings
66 |
67 | If you are behind a firewall you will need to set the proxy
68 | This is configured in the app.yml file.
69 |
70 | ## Authorization
71 |
72 | Here is an example of the authorization annotation as used in ```PrivateInfoResource```.
73 |
74 | ```java
75 | /**
76 | * @return The private home view if authenticated
77 | */
78 | @GET
79 | @Path("/home")
80 | @Timed
81 | @CacheControl(noCache = true)
82 | public PublicFreemarkerView viewHome(
83 | @RestrictedTo(Authority.ROLE_PUBLIC)
84 | User publicUser) {
85 |
86 | BaseModel model = newBaseModel();
87 | return new PublicFreemarkerView("private/home.ftl", model);
88 |
89 | }
90 | ```
91 |
92 | ## Adding yourself as an "admin"
93 |
94 | The code supports different levels of authority (PUBLIC and ADMIN).
95 | To add yourself as an admin, add your email address to the list of adminUsers in app.yml.
96 |
97 | The user is uniquely identified by email address and provider ID; if you only check the
98 | email address, they might be able to find a provider that lets them enter your email address.
99 | If you're using openid, use "openid" for the provider ID.
100 |
101 | Note that you can't use a Twitter login for specifying an admin because there's no
102 | way to get the user's email address. This is because Twitter is too lazy to implement
103 | a permissions system like Facebook/Google have. [Gripe to Twitter](https://dev.twitter.com/discussions/1737)
104 | if you think this is stupid.
105 |
106 | ## General code layout
107 |
108 | * Configuration class for app.yaml is in src/main/java/OpenIDDemoConfiguration.java
109 | * The standard Dropwizard service/app is src/main/java/OpenIDDemoService.java
110 | * OpenID user classes are used for both OpenID and OAuth despite the naming (which came
111 | from Gary Rowe's original project); I did minimal renames initially, just in case these
112 | changes are merged into the original project
113 | * The in-mem user cache is in src/main/java/core/*.java
114 | * The user model is in src/main/java/model/security/User.java
115 | * The REST path handlers are in src/main/java/resources/*.java as
116 | usual for DropWizard projects.
117 | The OAuth rest handler is in src/main/java/resources/PublicOAuthResource.java.
118 | * The model used for the FreeMarker views is in src/main/java/model/BaseModel.java.
119 | It also holds anything that can be displayed on the small set of FreeMarker templates
120 | included.
121 | * The home page when you log in w/ all the social contacts/albums displayed is
122 | in src/main/resources/views/ftl/private/home.ftl. The contacts/albums should not
123 | be loaded this way in a real application because it takes one second...sometimes way
124 | over if you have a lot :-) In a real application, the social info should be AJAX
125 | loaded in the background but this was done for simpler testing.
126 | * Bootstrap was added by modifying the resources/views/ftl/includes/common header.ftl
127 | and cdn-scripts.ftl and header.ftl and footer.ftl files.
128 |
129 | ## Changes to Dropwizard-OpenID
130 |
131 | * Cleaned up warning msgs
132 | * Added Gradle support, though currently you have to comment out the
133 | shadow plugin references to import into Eclipse because of a shadow plugin
134 | bug with a create method in that plugin or a bug in the Eclipse Gradle plugin :-(
135 | * Added User to BaseResource so pages can display a logout button if you're logged in
136 | * Added Bootstrap via a CDN to make templates look better
137 | * Moved login.ftl from openid subdir to common
138 | * Added logout support
139 | * Added support for configuring OAuth providers/permissions via the app.yml
140 | * Moved /login and /logout REST APIs from /openid to / (into PublicHomeResource)
141 | * Added images for facebook/google/twitter/linkedin OAuth buttons
142 | * Added logout button and user state to all BaseModels
143 | * Added adminUsers config in app.yml
144 | * Added display of user's contact list and albums to /private/home
145 | (note that SocialAuth doesn't currently have a Google+ plugin for these)
146 | * Added proxy config to app.yml
147 | * Added custom SocialAuth OAuth properties to app.yml
148 | * Moved cookie generator to BaseResource since it's used by OpenID and OAuth
149 |
--------------------------------------------------------------------------------
/app.yml:
--------------------------------------------------------------------------------
1 | # Define the HTTP settings
2 | http:
3 | port: 8080
4 | adminPort: 8081
5 |
6 | # Define proxy host settings for connecting to OpenID servers in PublicOpenIDResource
7 | ##proxyHost: proxy.domain.com
8 | ##proxyPort: 8080
9 |
10 | # Define the client settings for connecting to upstream data services
11 | httpClient:
12 | timeout: 60s # Timeout while connecting, reading, or writing (Heroku needs large value for spin-up)
13 | timeToLive: 10m # Keep connections open
14 | cookiesEnabled: true # Track cookies
15 | gzipEnabled: true # Allow for gzipped request and response entities
16 | minThreads: 1
17 | maxThreads: 128 # Thread pool for JerseyClient's async requests
18 |
19 | logging:
20 |
21 | level: WARN
22 |
23 | # Logger-specific levels.
24 | loggers:
25 |
26 | # Set specific levels
27 | "com.sun.jersey.api.client": DEBUG
28 | "com.yammer": INFO
29 | "uk.co.froot": DEBUG
30 |
31 | # ...
32 | # Settings for logging to stdout.
33 | console:
34 |
35 | # If true, write log statements to stdout.
36 | enabled: true
37 |
38 | # Do not display log statements below this threshold to stdout.
39 | threshold: ALL
40 |
41 | # Settings for logging to a file.
42 | file:
43 |
44 | # If true, write log statements to a file.
45 | enabled: false
46 |
47 | # Do not write log statements below this threshold to the file.
48 | threshold: ALL
49 |
50 | # The file to which current statements will be logged.
51 | currentLogFilename: /var/log/example/developer.log
52 |
53 | # When the log file rotates, the archived log will be renamed to this and gzipped. The
54 | # %d is replaced with the previous day (yyyy-MM-dd). Custom rolling windows can be created
55 | # by passing a SimpleDateFormat-compatible format as an argument: "%d{yyyy-MM-dd-hh}".
56 | archivedLogFilenamePattern: /var/log/example/developer-%d.log.gz
57 |
58 | # The number of archived files to keep.
59 | archivedFileCount: 5
60 |
61 | # The timezone used to format dates. HINT: USE THE DEFAULT, UTC.
62 | timeZone: UTC
63 |
64 | # Define OAuth settings
65 | oauthSuccessUrl: http://localhost:8080/oauth/verify
66 |
67 | oauthCfg:
68 | - name: googleplus
69 | prefix: googleapis.com
70 | key: 1111111.apps.googleusercontent.com
71 | secret: 22222222
72 | permissions: https://www.googleapis.com/auth/userinfo.profile,email,http://www.google.com/m8/feeds/,http://picasaweb.google.com/data/
73 | ## Valid permissions/scopes from: http://support.google.com/a/bin/answer.py?hl=en&answer=162106
74 | - name: facebook
75 | prefix: graph.facebook.com
76 | key: 1111111
77 | secret: 222222222
78 | permissions: publish_stream,read_stream,user_photos,email,user_birthday,user_location,offline_access
79 | ## Valid permissions from https://developers.facebook.com/docs/reference/login/extended-permissions/
80 | - name: twitter
81 | prefix: twitter.com
82 | key: 111111
83 | secret: 22222222
84 | - name: linkedin
85 | prefix: api.linkedin.com
86 | key: 11111111
87 | secret: 222222222
88 |
89 | #oauthCustomCfg:
90 | # socialauth.myprovider: org.my.socialauth.provider.SomethingImpl
91 | # socialauth.myprovider.scope: user_email
92 |
93 | # Define admin users (email address and provider uniquely identifies a user)
94 | adminUsers:
95 | test@test.com: facebook
96 | name@name.com: googleplus
97 |
98 | #
99 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | import org.apache.tools.ant.taskdefs.condition.Os
2 |
3 | apply plugin: 'shadow'
4 |
5 | apply plugin: 'java'
6 | apply plugin: 'idea'
7 | apply plugin: 'eclipse'
8 |
9 | project.sourceCompatibility = 1.7
10 | project.targetCompatibility = 1.7
11 |
12 | repositories {
13 | mavenCentral()
14 | }
15 |
16 | configurations {
17 | /** Guice 2.0 has been superseded...this is in openid4java which needs to be updated **/
18 | all*.exclude group: 'com.google.code.guice'
19 | }
20 |
21 | dependencies {
22 | compile "com.yammer.dropwizard:dropwizard-core:$dropwizardVersion"
23 | /** Authentication support **/
24 | compile "com.yammer.dropwizard:dropwizard-auth:$dropwizardVersion"
25 | /** Resource testing support **/
26 | compile "com.yammer.dropwizard:dropwizard-testing:$dropwizardVersion"
27 | /** HTML Freemarker views support **/
28 | compile "com.yammer.dropwizard:dropwizard-views:$dropwizardVersion"
29 | /** REST client support for upstream data **/
30 | compile "com.yammer.dropwizard:dropwizard-client:$dropwizardVersion"
31 |
32 | /** OpenID library **/
33 | /** Guice 2.0 has been superseded **/
34 | compile ('org.openid4java:openid4java-consumer:0.9.6') {
35 | exclude group: 'com.google.code.guice'
36 | }
37 | compile "com.google.inject:guice:3.0"
38 |
39 | /** OAuth library **/
40 | compile "org.brickred:socialauth:4.+";
41 | compile "org.brickred:socialauth-filter:2.+";
42 |
43 | /** markdown support for web pages **/
44 | compile "org.pegdown:pegdown:1.1.0"
45 | }
46 |
47 |
48 | jar {
49 | manifest {
50 | attributes 'Main-Class': pkgname+'.'+appclass
51 | }
52 | }
53 |
54 | /**
55 | * One-JAR plugin integration
56 | */
57 | buildscript {
58 | repositories {
59 | maven {
60 | name 'Gradle Shadow'
61 | url 'http://dl.bintray.com/content/johnrengelman/gradle-plugins'
62 | }
63 | }
64 | dependencies {
65 | classpath 'org.gradle.plugins:shadow:0.7.4'
66 | }
67 | }
68 |
69 | import org.gradle.api.plugins.shadow.transformers.AppendingTransformer
70 | shadow {
71 | exclude 'META-INF/*.SF'
72 | exclude 'META-INF/*.DSA'
73 | exclude 'META-INF/*.RSA'
74 | transformer(AppendingTransformer) {
75 | resource = 'META-INF/spring.handlers'
76 | }
77 | transformer(AppendingTransformer) {
78 | resource = 'META-INF/spring.schemas'
79 | }
80 | }
81 |
82 | task run(dependsOn: 'classes', type: JavaExec) {
83 | main = pkgname+'.'+appclass
84 | classpath = sourceSets.main.runtimeClasspath
85 | args 'server','app.yml'
86 | }
87 |
88 | project.ext {
89 | oneJarName = jarprefix+'-'+version+'-standalone.jar'
90 | shadowJarName = jarprefix+'-'+version+'-shadow.jar'
91 | }
92 |
93 | task runShadow(type:Exec, dependsOn: 'shadow') {
94 | workingDir = "build/libs"
95 | commandLine = ['java', '-jar', '-server', project.shadowJarName,'server','../../app.yml']
96 | }
97 |
98 | task bdd(type: Exec, dependsOn: 'shadow') {
99 | description = "Tests BDDs against the running application"
100 |
101 | // fix to run cucumber on a particular infernal OS properly
102 | if (Os.isFamily(Os.FAMILY_WINDOWS)) {
103 | commandLine 'cmd', '/c', 'bundle.bat', 'exec', 'cucumber'
104 | }
105 | else {
106 | commandLine = ['cucumber']
107 | }
108 | workingDir = "./src/test/resources/bdd"
109 | def jarProcess = null
110 |
111 | doFirst{
112 | println "Starting application on separate thread..."
113 | Thread.startDaemon {
114 | jarProcess = 'java -jar -server build/libs/'+project.shadowJarName+' server app.yml'.execute()
115 | }
116 | println "Waiting for application to start before executing tests..."
117 | addShutdownHook {
118 | println "Shutting down application"
119 | jarProcess.destroy()
120 | }
121 | }
122 | }
123 |
124 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #org.gradle.daemon=true
2 |
3 | version = 1.2.0
4 | pkgname = uk.co.froot.demo.openid
5 | appclass = OpenIDDemoService
6 | jarprefix = dropwizard-oauth-openid
7 |
8 | dropwizardVersion = 0.6.2
9 |
10 | springVersion = 3.2.4.RELEASE
11 | springSecurityVersion = 3.1.4.RELEASE
12 |
13 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 | 4.1.0
5 |
6 | uk.co.froot.demo
7 | dropwizard-openid
8 | 1.2.0
9 |
10 | http://localhost:8080
11 |
12 | OpenId/OAuth Service
13 | A demo using Dropwizard and OpenId4Java/SocialAuth
14 | 2013
15 |
16 |
17 |
18 | 0.6.2
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | org.apache.maven.plugins
28 | maven-compiler-plugin
29 | 2.3
30 |
31 | 1.6
32 | 1.6
33 | true
34 |
35 |
36 |
37 |
38 |
39 | org.apache.maven.plugins
40 | maven-shade-plugin
41 | 1.6
42 |
43 | true
44 |
45 |
46 | *:*
47 |
48 | META-INF/*.SF
49 | META-INF/*.DSA
50 | META-INF/*.RSA
51 |
52 |
53 |
54 |
55 |
56 |
57 | package
58 |
59 | shade
60 |
61 |
62 |
63 |
64 |
65 | uk.co.froot.demo.openid.OpenIDDemoService
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | com.yammer.dropwizard
81 | dropwizard-core
82 | ${dropwizard.version}
83 |
84 |
85 |
86 | com.yammer.dropwizard
87 | dropwizard-auth
88 | ${dropwizard.version}
89 |
90 |
91 |
92 | com.yammer.dropwizard
93 | dropwizard-testing
94 | ${dropwizard.version}
95 |
96 |
97 |
98 | com.yammer.dropwizard
99 | dropwizard-views
100 | ${dropwizard.version}
101 |
102 |
103 |
104 | com.yammer.dropwizard
105 | dropwizard-client
106 | ${dropwizard.version}
107 |
108 |
109 |
110 |
111 | org.openid4java
112 | openid4java-consumer
113 | 0.9.6
114 | pom
115 |
116 |
117 |
118 | com.google.code.guice
119 | guice
120 |
121 |
122 |
123 |
124 |
125 |
126 | org.brickred
127 | socialauth
128 | 4.2
129 |
130 |
131 | org.brickred
132 | socialauth-filter
133 | 2.4
134 |
135 |
136 |
137 |
138 | com.google.inject
139 | guice
140 | 3.0
141 |
142 |
143 |
144 |
145 | org.pegdown
146 | pegdown
147 | 1.1.0
148 |
149 |
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/AppConfiguration.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid;
2 |
3 | import java.util.HashMap;
4 | import java.util.List;
5 | import java.util.Map;
6 | import java.util.Properties;
7 |
8 | import javax.validation.Valid;
9 | import javax.validation.constraints.NotNull;
10 |
11 | import org.hibernate.validator.constraints.NotEmpty;
12 |
13 | import com.fasterxml.jackson.annotation.JsonProperty;
14 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
15 | import com.yammer.dropwizard.client.JerseyClientConfiguration;
16 | import com.yammer.dropwizard.config.Configuration;
17 |
18 | /**
19 | * DropWizard Configuration to provide the following to application:
20 | *
21 | * Initialisation code
22 | *
23 | *
24 | * @since 0.0.1
25 | *
26 | */
27 | public class AppConfiguration extends Configuration {
28 |
29 | /**
30 | * The cookie name for the session token
31 | */
32 | public static final String SESSION_TOKEN_NAME ="OpenIDDemo-Session";
33 |
34 | @NotEmpty
35 | @JsonProperty
36 | private String assetCachePolicy="maximumSize=10000, expireAfterAccess=5s";
37 |
38 | /**
39 | * How long a session cookie authentication can remain inactive before the user must signin in
40 | * TODO Implement this
41 | */
42 | @NotEmpty
43 | @JsonProperty
44 | private String cookieAuthenticationCachePolicy ="maximumSize=10000, expireAfterAccess=600s";
45 |
46 | @Valid
47 | @NotNull
48 | @JsonProperty
49 | private JerseyClientConfiguration httpClient = new JerseyClientConfiguration();
50 |
51 | public String getAssetCachePolicy() {
52 | return assetCachePolicy;
53 | }
54 |
55 | public JerseyClientConfiguration getJerseyClientConfiguration() {
56 | return httpClient;
57 | }
58 |
59 | public String getCookieAuthenticationCachePolicy() {
60 | return cookieAuthenticationCachePolicy;
61 | }
62 |
63 | @JsonProperty private String oauthSuccessUrl = "";
64 | public String getOAuthSuccessUrl() {
65 | return oauthSuccessUrl;
66 | }
67 |
68 | public static class OAuthCfgClass {
69 | @JsonProperty private String url;
70 | @JsonProperty private String name;
71 | @JsonProperty private String prefix;
72 | @JsonProperty private String key;
73 | @JsonProperty private String secret;
74 | @JsonProperty private String permissions;
75 |
76 | public String getUrl() {
77 | return url;
78 | }
79 | public String getName() {
80 | return name;
81 | }
82 | public String getPrefix() {
83 | return prefix;
84 | }
85 | public String getKey() {
86 | return key;
87 | }
88 | public String getSecret() {
89 | return secret;
90 | }
91 | public String getPermissions() {
92 | return permissions;
93 | }
94 | }
95 | @JsonDeserialize(contentAs = OAuthCfgClass.class)
96 | private List oauthCfg;
97 | public List getOAuthCfg() {
98 | return oauthCfg;
99 | }
100 |
101 | @JsonProperty
102 | private HashMap oauthCustomCfg = null;
103 | public Map OAuthCustomCfg() {
104 | return oauthCustomCfg;
105 | }
106 |
107 | public Properties getOAuthCfgProperties() {
108 | Properties properties = new Properties();
109 | for (OAuthCfgClass oauth : oauthCfg) {
110 | properties.put(oauth.getPrefix() + ".consumer_key",
111 | oauth.getKey());
112 | properties.put(oauth.getPrefix() + ".consumer_secret",
113 | oauth.getSecret());
114 | if (oauth.getPermissions() != null) {
115 | properties.put(oauth.getPrefix() + ".custom_permissions",
116 | oauth.getPermissions());
117 | }
118 | }
119 | if (oauthCustomCfg != null) {
120 | // add any custom config strings
121 | properties.putAll(oauthCustomCfg);
122 | }
123 | return properties;
124 | }
125 |
126 | @JsonProperty
127 | private HashMap adminUsers = null;
128 | public Map getAdminUsers() {
129 | return adminUsers;
130 | }
131 |
132 | @JsonProperty
133 | private String proxyHost = null;
134 | public String getProxyHost() {
135 | return proxyHost;
136 | }
137 |
138 | @JsonProperty
139 | private int proxyPort = 8080;
140 | public int getProxyPort() {
141 | return proxyPort;
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/OpenIDDemoService.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid;
2 |
3 | import com.yammer.dropwizard.Service;
4 | import com.yammer.dropwizard.assets.AssetsBundle;
5 | import com.yammer.dropwizard.config.Bootstrap;
6 | import com.yammer.dropwizard.config.Environment;
7 | import com.yammer.dropwizard.views.ViewBundle;
8 | import com.yammer.dropwizard.views.ViewMessageBodyWriter;
9 | import org.eclipse.jetty.server.session.SessionHandler;
10 | import uk.co.froot.demo.openid.auth.openid.OpenIDAuthenticator;
11 | import uk.co.froot.demo.openid.auth.openid.OpenIDRestrictedToProvider;
12 | import uk.co.froot.demo.openid.model.security.User;
13 | import uk.co.froot.demo.openid.resources.PublicHomeResource;
14 |
15 | /**
16 | *
17 | * Service to provide the following to application:
18 | *
19 | *
20 | * Provision of access to resources
21 | *
22 | *
23 | * Use java -jar mbm-develop-SNAPSHOT.jar server openid-demo.yml
to
24 | * start the demo
25 | *
26 | *
27 | * @since 0.0.1
28 | */
29 | public class OpenIDDemoService extends Service {
30 | private static AppConfiguration cfg;
31 |
32 | public static AppConfiguration getConfig() {
33 | return cfg;
34 | }
35 |
36 | /**
37 | * Main entry point to the application
38 | *
39 | * @param args
40 | * CLI arguments
41 | * @throws Exception
42 | */
43 | public static void main(String[] args) throws Exception {
44 | new OpenIDDemoService().run(args);
45 | }
46 |
47 | private OpenIDDemoService() {
48 |
49 | }
50 |
51 | @Override
52 | public void initialize(Bootstrap configBootstrap) {
53 |
54 | // Bundles
55 | configBootstrap
56 | .addBundle(new AssetsBundle("/assets/images", "/images"));
57 | configBootstrap
58 | .addBundle(new AssetsBundle("/assets/jquery", "/jquery"));
59 | configBootstrap.addBundle(new ViewBundle());
60 | }
61 |
62 | @Override
63 | public void run(AppConfiguration appConfiguration, Environment environment)
64 | throws Exception {
65 | // save config so it can be used elseware
66 | cfg = appConfiguration;
67 |
68 | // Configure authenticator
69 | OpenIDAuthenticator authenticator = new OpenIDAuthenticator();
70 |
71 | // Configure environment
72 | environment
73 | .scanPackagesForResourcesAndProviders(PublicHomeResource.class);
74 |
75 | // Health checks
76 | environment
77 | .addHealthCheck(new uk.co.froot.demo.openid.health.OpenIdDemoHealthCheck());
78 |
79 | // Providers
80 | environment.addProvider(new ViewMessageBodyWriter());
81 | environment.addProvider(new OpenIDRestrictedToProvider(
82 | authenticator, "OpenID"));
83 |
84 | // Session handler
85 | environment.setSessionHandler(new SessionHandler());
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/auth/annotation/RestrictedTo.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.auth.annotation;
2 |
3 | import uk.co.froot.demo.openid.model.security.Authority;
4 |
5 | import java.lang.annotation.*;
6 |
7 | /**
8 | * Annotation to provide the following to application:
9 | *
10 | * Concise type-safe reference to {@link uk.co.froot.demo.openid.model.security.Authority}
11 | * Binds to parameter to assist injection of User
12 | *
13 | * Example:
14 | * {@code
15 | * public void doSomething(
16 | * @RestrictedTo({ROLE_ADMIN, ROLE_PUBLIC})
17 | * User user
18 | * )
19 | * }
20 | * Would require a User with both authorities to be able to access the method
21 | * The default Authority is ROLE_ADMIN indicating that if no authority is specified then only an admin can reach the resource (fail safe).
22 | *
23 | * @since 0.0.1
24 | *
25 | */
26 |
27 | @Documented
28 | @Retention(RetentionPolicy.RUNTIME)
29 | @Target({ElementType.PARAMETER})
30 | public @interface RestrictedTo {
31 | Authority[] value() default Authority.ROLE_ADMIN;
32 | }
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/auth/openid/OpenIDAuthenticator.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.auth.openid;
2 |
3 | import com.google.common.base.Optional;
4 | import com.yammer.dropwizard.auth.AuthenticationException;
5 | import com.yammer.dropwizard.auth.Authenticator;
6 | import uk.co.froot.demo.openid.core.InMemoryUserCache;
7 | import uk.co.froot.demo.openid.model.security.User;
8 |
9 | /**
10 | * Authenticator to provide the following to application:
11 | *
12 | * Verifies the provided credentials are valid
13 | *
14 | *
15 | * @since 0.0.1
16 | */
17 | public class OpenIDAuthenticator implements Authenticator {
18 |
19 | @Override
20 | public Optional authenticate(OpenIDCredentials credentials) throws AuthenticationException {
21 |
22 | // Get the User referred to by the API key
23 | Optional user = InMemoryUserCache
24 | .INSTANCE
25 | .getBySessionToken(credentials.getSessionToken());
26 | if (!user.isPresent()) {
27 | return Optional.absent();
28 | }
29 |
30 | // Check that their authorities match their credentials
31 | if (!user.get().hasAllAuthorities(credentials.getRequiredAuthorities())) {
32 | return Optional.absent();
33 | }
34 |
35 | return user;
36 |
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/auth/openid/OpenIDCredentials.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.auth.openid;
2 |
3 | import com.google.common.base.Objects;
4 | import uk.co.froot.demo.openid.model.security.Authority;
5 |
6 | import java.util.Set;
7 | import java.util.UUID;
8 |
9 | import static com.google.common.base.Preconditions.checkNotNull;
10 |
11 | /**
12 | * Value object to provide the following to {@link OpenIDAuthenticator}:
13 | *
14 | * Storage of the necessary credentials for OpenID authentication
15 | *
16 | *
17 | * @since 0.0.1
18 | */
19 | public class OpenIDCredentials {
20 |
21 | private final UUID sessionToken;
22 | private final Set requiredAuthorities;
23 |
24 | /**
25 | * @param sessionToken The session token acting as a surrogate for the OpenID token
26 | * @param requiredAuthorities The authorities required to authenticate (provided by the {@link uk.co.froot.demo.openid.auth.annotation.RestrictedTo} annotation)
27 | */
28 | public OpenIDCredentials(
29 | UUID sessionToken,
30 | Set requiredAuthorities) {
31 | this.sessionToken = checkNotNull(sessionToken);
32 | this.requiredAuthorities = checkNotNull(requiredAuthorities);
33 | }
34 |
35 | /**
36 | * @return The OpenID token
37 | */
38 | public UUID getSessionToken() {
39 | return sessionToken;
40 | }
41 |
42 | /**
43 | * @return The authorities required to successfully authenticate
44 | */
45 | public Set getRequiredAuthorities() {
46 | return requiredAuthorities;
47 | }
48 |
49 | @Override
50 | public boolean equals(Object obj) {
51 | if (this == obj) {
52 | return true;
53 | }
54 | if ((obj == null) || (getClass() != obj.getClass())) {
55 | return false;
56 | }
57 | final OpenIDCredentials that = (OpenIDCredentials) obj;
58 |
59 | return sessionToken.equals(that.sessionToken);
60 | }
61 |
62 | @Override
63 | public int hashCode() {
64 | return (31 * sessionToken.hashCode());
65 | }
66 |
67 | @Override
68 | public String toString() {
69 | return Objects.toStringHelper(this)
70 | .add("sessionId", sessionToken)
71 | .add("authorities", requiredAuthorities)
72 | .toString();
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/auth/openid/OpenIDRestrictedToInjectable.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.auth.openid;
2 |
3 |
4 | import com.google.common.base.Optional;
5 | import com.google.common.collect.Sets;
6 | import com.sun.jersey.api.core.HttpContext;
7 | import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
8 | import com.yammer.dropwizard.auth.AuthenticationException;
9 | import com.yammer.dropwizard.auth.Authenticator;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 | import uk.co.froot.demo.openid.AppConfiguration;
13 | import uk.co.froot.demo.openid.model.security.Authority;
14 |
15 | import javax.ws.rs.WebApplicationException;
16 | import javax.ws.rs.core.Cookie;
17 | import javax.ws.rs.core.Response;
18 | import java.util.Arrays;
19 | import java.util.Map;
20 | import java.util.Set;
21 | import java.util.UUID;
22 |
23 | /**
24 | * Injectable to provide the following to {@link OpenIDRestrictedToProvider}:
25 | *
26 | * Performs decode from HTTP request
27 | * Carries OpenID authentication data
28 | *
29 | *
30 | * @since 0.0.1
31 | */
32 | class OpenIDRestrictedToInjectable extends AbstractHttpContextInjectable {
33 |
34 | private static final Logger log = LoggerFactory.getLogger(OpenIDRestrictedToInjectable.class);
35 |
36 | private final Authenticator authenticator;
37 | private final String realm;
38 | private final Set requiredAuthorities;
39 |
40 | /**
41 | * @param authenticator The Authenticator that will compare credentials
42 | * @param realm The authentication realm
43 | * @param requiredAuthorities The required authorities as provided by the RestrictedTo annotation
44 | */
45 | OpenIDRestrictedToInjectable(
46 | Authenticator authenticator,
47 | String realm,
48 | Authority[] requiredAuthorities) {
49 | this.authenticator = authenticator;
50 | this.realm = realm;
51 | this.requiredAuthorities = Sets.newHashSet(Arrays.asList(requiredAuthorities));
52 | }
53 |
54 | public Authenticator getAuthenticator() {
55 | return authenticator;
56 | }
57 |
58 | public String getRealm() {
59 | return realm;
60 | }
61 |
62 | public Set getRequiredAuthorities() {
63 | return requiredAuthorities;
64 | }
65 |
66 | @Override
67 | public T getValue(HttpContext httpContext) {
68 |
69 | try {
70 |
71 | // Get the Authorization header
72 | final Map cookieMap = httpContext.getRequest().getCookies();
73 | if (!cookieMap.containsKey(AppConfiguration.SESSION_TOKEN_NAME)) {
74 | throw new WebApplicationException(Response.Status.UNAUTHORIZED);
75 | }
76 |
77 | UUID sessionToken = UUID.fromString(cookieMap.get(AppConfiguration.SESSION_TOKEN_NAME).getValue());
78 |
79 | if (sessionToken != null) {
80 |
81 | final OpenIDCredentials credentials = new OpenIDCredentials(sessionToken, requiredAuthorities);
82 |
83 | final Optional result = authenticator.authenticate(credentials);
84 | if (result.isPresent()) {
85 | return result.get();
86 | }
87 | }
88 | } catch (IllegalArgumentException e) {
89 | log.debug("Error decoding credentials",e);
90 | } catch (AuthenticationException e) {
91 | log.warn("Error authenticating credentials",e);
92 | throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
93 | }
94 |
95 | // Must have failed to be here
96 | throw new WebApplicationException(Response.Status.UNAUTHORIZED);
97 | }
98 |
99 | }
100 |
101 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/auth/openid/OpenIDRestrictedToProvider.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.auth.openid;
2 |
3 |
4 | import com.sun.jersey.api.model.Parameter;
5 | import com.sun.jersey.core.spi.component.ComponentContext;
6 | import com.sun.jersey.core.spi.component.ComponentScope;
7 | import com.sun.jersey.spi.inject.Injectable;
8 | import com.sun.jersey.spi.inject.InjectableProvider;
9 | import com.yammer.dropwizard.auth.Authenticator;
10 | import uk.co.froot.demo.openid.auth.annotation.RestrictedTo;
11 |
12 | /**
13 | * Authentication provider to provide the following to Jersey:
14 | *
15 | * Bridge between Dropwizard and Jersey for HMAC authentication
16 | * Provides additional {@link uk.co.froot.demo.openid.model.security.Authority} information
17 | *
18 | *
19 | * @param the principal type.
20 | * @since 0.0.1
21 | */
22 | public class OpenIDRestrictedToProvider implements InjectableProvider {
23 |
24 | private final Authenticator authenticator;
25 | private final String realm;
26 |
27 | /**
28 | * Creates a new {@link OpenIDRestrictedToProvider} with the given {@link com.yammer.dropwizard.auth.Authenticator} and realm.
29 | *
30 | * @param authenticator the authenticator which will take the {@link OpenIDCredentials} and
31 | * convert them into instances of {@code T}
32 | * @param realm the name of the authentication realm
33 | */
34 | public OpenIDRestrictedToProvider(Authenticator authenticator, String realm) {
35 | this.authenticator = authenticator;
36 | this.realm = realm;
37 | }
38 |
39 | @Override
40 | public ComponentScope getScope() {
41 | return ComponentScope.PerRequest;
42 | }
43 |
44 | @Override
45 | public Injectable> getInjectable(ComponentContext ic,
46 | RestrictedTo a,
47 | Parameter c) {
48 | return new OpenIDRestrictedToInjectable(authenticator, realm, a.value());
49 | }
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/core/InMemoryOpenIDCache.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.core;
2 |
3 | import com.google.common.base.Optional;
4 | import com.google.common.cache.Cache;
5 | import com.google.common.cache.CacheBuilder;
6 | import org.openid4java.consumer.ConsumerManager;
7 |
8 | import java.util.UUID;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | /**
12 | * In-memory cache to provide the following to OpenID authentication:
13 | *
14 | * Short term storage of thread local session data
15 | *
16 | *
17 | * @since 0.0.1
18 | *
19 | */
20 | public enum InMemoryOpenIDCache {
21 |
22 | INSTANCE;
23 |
24 | /**
25 | * Simple cache for {@link org.openid4java.consumer.ConsumerManager} entries
26 | */
27 | private final Cache consumerManagerCache = CacheBuilder.newBuilder()
28 | .maximumSize(1000)
29 | .expireAfterWrite(2, TimeUnit.MINUTES)
30 | .build();
31 |
32 | /**
33 | * Note that this is not horizontally scalable
34 | *
35 | * @param sessionToken The session token
36 | *
37 | * @return The mapped OpenID {@link org.openid4java.consumer.ConsumerManager} created for that session token
38 | */
39 | public Optional getConsumerManager(UUID sessionToken) {
40 |
41 | return Optional.fromNullable(consumerManagerCache.getIfPresent(sessionToken));
42 |
43 | }
44 |
45 | /**
46 | * Note that this is not horizontally scalable
47 | *
48 | * @param sessionToken The session token
49 | * @param consumerManager The OpenID ConsumerManager for this UUID
50 | */
51 | public void putConsumerManager(UUID sessionToken, ConsumerManager consumerManager) {
52 |
53 | consumerManagerCache.put(sessionToken, consumerManager);
54 |
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/core/InMemoryUserCache.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.core;
2 |
3 | import com.google.common.base.Optional;
4 | import com.google.common.base.Preconditions;
5 | import com.google.common.cache.Cache;
6 | import com.google.common.cache.CacheBuilder;
7 | import uk.co.froot.demo.openid.model.security.User;
8 |
9 | import java.util.Map;
10 | import java.util.UUID;
11 | import java.util.concurrent.TimeUnit;
12 |
13 | /**
14 | * Cache to provide the following to {@link User} authenticators:
15 | *
16 | * In-memory thread-safe cache for client user instances
17 | * Provision of fast lookup for request authentication
18 | *
19 | *
20 | * @since 0.0.1
21 | *
22 | */
23 | public enum InMemoryUserCache {
24 |
25 | // Provide a global singleton for the application
26 | INSTANCE;
27 |
28 | // A lot of threads will hit this cache
29 | private volatile Cache userCache;
30 |
31 | InMemoryUserCache() {
32 | reset(15, TimeUnit.MINUTES);
33 | }
34 |
35 | /**
36 | * Resets the cache and allows the expiry time to be set (perhaps for testing)
37 | *
38 | * @param duration The duration before a user must manually authenticate through a web form due to inactivity
39 | * @param unit The {@link java.util.concurrent.TimeUnit} that duration is expressed in
40 | */
41 | public InMemoryUserCache reset(int duration, TimeUnit unit) {
42 |
43 | // Build the cache
44 | if (userCache != null) {
45 | userCache.invalidateAll();
46 | }
47 |
48 | // If there is no activity against a key then we want
49 | // it to be expired from the cache, but each fresh write
50 | // will reset the expiry timer
51 | userCache = CacheBuilder
52 | .newBuilder()
53 | .expireAfterWrite(duration, unit)
54 | .maximumSize(1000)
55 | .build();
56 |
57 | return INSTANCE;
58 | }
59 |
60 | /**
61 | * @param sessionToken The session token to locate the user (not JSESSIONID)
62 | *
63 | * @return The matching User or absent
64 | */
65 | public Optional getBySessionToken(UUID sessionToken) {
66 |
67 | // Check the cache
68 | Optional userOptional = Optional.fromNullable(userCache.getIfPresent(sessionToken));
69 |
70 | if (userOptional.isPresent()) {
71 | // Ensure we refresh the cache on a check to maintain the session timeout
72 | userCache.put(sessionToken, userOptional.get());
73 | }
74 |
75 | return userOptional;
76 |
77 | }
78 |
79 | /**
80 | * @param sessionToken The session token to use to locate the user
81 | * @param user The User to cache
82 | */
83 | public void put(UUID sessionToken, User user) {
84 |
85 | Preconditions.checkNotNull(user);
86 |
87 | userCache.put(sessionToken, user);
88 | }
89 |
90 | public void hardDelete(User user) {
91 |
92 | Preconditions.checkNotNull(user);
93 | Preconditions.checkNotNull(user.getSessionToken());
94 |
95 | userCache.invalidate(user.getSessionToken());
96 | }
97 |
98 | public Optional getByOpenIDIdentifier(String openIDIdentifier) {
99 |
100 | Map map = userCache.asMap();
101 |
102 | for (Map.Entry entry : map.entrySet()) {
103 |
104 | if (entry.getValue().getOpenIDIdentifier().equals(openIDIdentifier)) {
105 | return Optional.of(entry.getValue());
106 | }
107 |
108 | }
109 |
110 | return Optional.absent();
111 | }
112 |
113 | public Optional getByEmailAddress(String emailAddress) {
114 | Map map = userCache.asMap();
115 |
116 | for (Map.Entry entry : map.entrySet()) {
117 |
118 | if (entry.getValue().getEmailAddress().equals(emailAddress)) {
119 | return Optional.of(entry.getValue());
120 | }
121 |
122 | }
123 |
124 | return Optional.absent();
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/health/OpenIdDemoHealthCheck.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.health;
2 |
3 | /**
4 | * HealthCheck to provide the following to application:
5 | *
6 | * Provision of checks against a given Configuration property
7 | *
8 | *
9 | * @since 0.0.1
10 | *
11 | */
12 |
13 | import com.yammer.metrics.core.HealthCheck;
14 |
15 | public class OpenIdDemoHealthCheck extends HealthCheck {
16 |
17 | public OpenIdDemoHealthCheck() {
18 | super("OpenID Demo");
19 | }
20 |
21 | @Override
22 | protected Result check() throws Exception {
23 | return Result.healthy();
24 | }
25 | }
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/model/BaseModel.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.model;
2 |
3 | import java.io.IOException;
4 | import java.net.URL;
5 | import java.util.List;
6 |
7 | import org.brickred.socialauth.Album;
8 | import org.brickred.socialauth.AuthProvider;
9 | import org.brickred.socialauth.AuthProviderFactory;
10 | import org.brickred.socialauth.Contact;
11 | import org.brickred.socialauth.plugin.AlbumsPlugin;
12 | import org.brickred.socialauth.util.AccessGrant;
13 | import org.pegdown.PegDownProcessor;
14 | import org.slf4j.Logger;
15 | import org.slf4j.LoggerFactory;
16 |
17 | import uk.co.froot.demo.openid.AppConfiguration;
18 | import uk.co.froot.demo.openid.AppConfiguration.OAuthCfgClass;
19 | import uk.co.froot.demo.openid.OpenIDDemoService;
20 | import uk.co.froot.demo.openid.model.security.User;
21 |
22 | import com.google.common.base.Charsets;
23 | import com.google.common.io.Resources;
24 |
25 | /**
26 | *
27 | * Base class to provide the following to views:
28 | *
29 | *
30 | * Access to common data (user, adverts etc)
31 | *
32 | * @since 0.0.1
33 | */
34 | public class BaseModel {
35 | private static final Logger log = LoggerFactory
36 | .getLogger(BaseModel.class);
37 |
38 | public BaseModel() {
39 | AppConfiguration cfg = OpenIDDemoService.getConfig();
40 | oauthCfg = cfg.getOAuthCfg();
41 | }
42 |
43 | private User user;
44 |
45 | public User getUser() {
46 | return user;
47 | }
48 |
49 | public void setUser(User user) {
50 | this.user = user;
51 | }
52 |
53 | // list of OAuth configs is used for display on login page
54 | // probably should be moved to a new LoginPageModel
55 | private List oauthCfg;
56 |
57 | public List getOAuthCfg() {
58 | return oauthCfg;
59 | }
60 |
61 | // list of social network contacts
62 | private List contacts = null;
63 | public List getContacts() {
64 | return contacts;
65 | }
66 |
67 | // list of social network albums
68 | private List albums = null;
69 | public List getAlbums() {
70 | return albums;
71 | }
72 |
73 | /**
74 | * @return Some Markdown rendered as HTML - this is an inefficient way of
75 | * performing this operation See the
76 | * /common/markdown.ftl
to see where it is displayed
77 | *
78 | * @throws IOException
79 | * If something goes wrong
80 | */
81 | public String getMarkdownHtml() throws IOException {
82 |
83 | URL url = BaseModel.class
84 | .getResource("/views/markdown/demo-all-elements.md");
85 | String markdown = Resources.toString(url, Charsets.UTF_8).trim();
86 |
87 | // New processor each time due to pegdown not being thread-safe
88 | // internally
89 | PegDownProcessor processor = new PegDownProcessor();
90 |
91 | // Return the rendered HTML
92 | return processor.markdownToHtml(markdown);
93 |
94 | }
95 |
96 | public void loadContacts() {
97 | if (getUser() != null) {
98 | try {
99 | if (getUser().getOAuthInfo() != null) {
100 | AccessGrant grant = getUser().getOAuthInfo();
101 | AuthProvider provider = AuthProviderFactory.getInstance(grant.getProviderId(),
102 | OpenIDDemoService.getConfig().getOAuthCfgProperties());
103 | provider.setAccessGrant(grant);
104 | provider.registerPlugins(); // this activates the plugins for this provider
105 | contacts = provider.getContactList();
106 | if (provider.getProviderId().equals("linkedin")) {
107 | // LinkedIn profiles are returned even if private w/ a private name
108 | // so scrub weird private contacts that linkedin's API returns
109 | for (int i = (contacts.size()-1); i >= 0; i--) {
110 | Contact contact = contacts.get(i);
111 | if (contact.getFirstName().equals("private")) {
112 | contacts.remove(i);
113 | }
114 | }
115 | // patch up bogus null DisplayName, profileURLs returned by LinkedIn plugin
116 | for (Contact contact: contacts) {
117 | if (contact.getProfileUrl() == null) {
118 | contact.setProfileUrl("#");
119 | }
120 | if ((contact.getDisplayName() == null) &&
121 | (contact.getFirstName() != null) &&
122 | (contact.getLastName() != null)) {
123 | contact.setDisplayName(contact.getFirstName() + " " + contact.getLastName());
124 | }
125 | }
126 | }
127 | }
128 | } catch (Exception e) {
129 | log.error("Error loading contacts: " + e);
130 | }
131 | }
132 | }
133 |
134 | public void loadAlbums() {
135 | if (getUser() != null) {
136 | try {
137 | if (getUser().getOAuthInfo() != null) {
138 | // use the accessgrant we stored away
139 | AccessGrant grant = getUser().getOAuthInfo();
140 | AuthProvider provider = AuthProviderFactory.getInstance(grant.getProviderId(),
141 | OpenIDDemoService.getConfig().getOAuthCfgProperties());
142 | provider.setAccessGrant(grant);
143 | provider.registerPlugins(); // this activates the plugins for this provider
144 | if (provider.isSupportedPlugin(org.brickred.socialauth.plugin.AlbumsPlugin.class)) {
145 | AlbumsPlugin p = provider.getPlugin(org.brickred.socialauth.plugin.AlbumsPlugin.class);
146 | albums = p.getAlbums();
147 | }
148 | }
149 | } catch (Exception e) {
150 | log.error("Error loading albums: " + e);
151 | }
152 | }
153 | }
154 |
155 | }
156 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/model/ModelBuilder.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.model;
2 |
3 | import java.util.UUID;
4 |
5 | import javax.ws.rs.core.Cookie;
6 | import javax.ws.rs.core.HttpHeaders;
7 |
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import uk.co.froot.demo.openid.AppConfiguration;
12 | import uk.co.froot.demo.openid.core.InMemoryUserCache;
13 | import uk.co.froot.demo.openid.model.security.User;
14 |
15 | import com.google.common.base.Optional;
16 |
17 | /**
18 | * Builder to provide the following to resources:
19 | *
20 | * Utility methods to build backing models for Views
21 | *
22 | *
23 | * @since 0.0.1
24 | */
25 | public class ModelBuilder {
26 |
27 | private static final Logger log = LoggerFactory.getLogger(ModelBuilder.class);
28 |
29 | public Optional extractSessionToken(HttpHeaders httpHeaders) {
30 |
31 | // Want to fail fast to absent() since this will get called a lot
32 | Optional sessionToken = Optional.absent();
33 |
34 | if (httpHeaders == null) {
35 | // Might be an internal call such as an error condition
36 | return sessionToken;
37 | }
38 | if (httpHeaders.getCookies() == null) {
39 | // This is a cold user
40 | return sessionToken;
41 | }
42 |
43 | Cookie cookie = httpHeaders.getCookies().get(AppConfiguration.SESSION_TOKEN_NAME);
44 | if (cookie == null) {
45 | // This is a cold user
46 | return sessionToken;
47 | }
48 | String value = cookie.getValue();
49 | if (value == null) {
50 | // This is a broken cookie
51 | // Rather than throw an error we can force a fresh login to fix it up
52 | return sessionToken;
53 | }
54 |
55 | // Must be OK to be here
56 | return Optional.of(UUID.fromString(value));
57 |
58 | }
59 |
60 | /**
61 | * @return A new base model with user populated from the session token if present
62 | */
63 | public BaseModel newBaseModel(HttpHeaders httpHeaders) {
64 |
65 | BaseModel baseModel = new BaseModel();
66 |
67 | // Locate and populate the user by their session token (if present)
68 | addUser(httpHeaders, baseModel);
69 |
70 | return baseModel;
71 |
72 | }
73 |
74 | /**
75 | *
76 | * @param httpHeaders The HTTP headers containing the session token
77 | * @param baseModel A base model
78 | */
79 | public void addUser(HttpHeaders httpHeaders, BaseModel baseModel) {
80 | Optional sessionToken = extractSessionToken(httpHeaders);
81 | if (sessionToken.isPresent()) {
82 | Optional user = InMemoryUserCache.INSTANCE.getBySessionToken(sessionToken.get());
83 | if (user.isPresent()) {
84 | log.debug("Found a user to match the session token {}",sessionToken.get());
85 | if (user.get().getSessionToken() == null) {
86 | user.get().setSessionToken(sessionToken.get());
87 | }
88 | baseModel.setUser(user.get());
89 | } else {
90 | log.debug("No user matching the session token {}",sessionToken.get());
91 | }
92 | }
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/model/openid/DiscoveryInformationMemento.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.model.openid;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import com.google.common.collect.Sets;
5 |
6 | import java.util.Set;
7 |
8 | /**
9 | * Memento to provide the following to OpenID authentication web handling:
10 | *
11 | * Persistence store of the discovery information
12 | *
13 | *
14 | * @since 0.0.1
15 | *
16 | */
17 | public class DiscoveryInformationMemento {
18 |
19 | /**
20 | * The OP endpoint URL.
21 | */
22 | @JsonProperty
23 | String opEndpoint;
24 |
25 | /**
26 | * The claimed identifier, i.e. the user's identity key.
27 | */
28 | @JsonProperty
29 | String claimedIdentifier;
30 |
31 | /**
32 | * The delegate, or OP-Local identifier.
33 | * The key through which the OP remembers the user's account.
34 | */
35 | @JsonProperty
36 | String delegate;
37 |
38 | /**
39 | * The OpenID protocol version, or target service type discovered through Yadis.
40 | */
41 | @JsonProperty
42 | String version;
43 |
44 | /**
45 | * All service types discovered for the endpoint.
46 | */
47 | @JsonProperty
48 | Set types = Sets.newLinkedHashSet();
49 |
50 | public String getOpEndpoint() {
51 | return opEndpoint;
52 | }
53 |
54 | public void setOpEndpoint(String opEndpoint) {
55 | this.opEndpoint = opEndpoint;
56 | }
57 |
58 | public String getClaimedIdentifier() {
59 | return claimedIdentifier;
60 | }
61 |
62 | public void setClaimedIdentifier(String claimedIdentifier) {
63 | this.claimedIdentifier = claimedIdentifier;
64 | }
65 |
66 | public String getDelegate() {
67 | return delegate;
68 | }
69 |
70 | public void setDelegate(String delegate) {
71 | this.delegate = delegate;
72 | }
73 |
74 | public String getVersion() {
75 | return version;
76 | }
77 |
78 | public void setVersion(String version) {
79 | this.version = version;
80 | }
81 |
82 | public Set getTypes() {
83 | return types;
84 | }
85 |
86 | public void setTypes(Set types) {
87 | this.types = types;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/model/openid/TokenMemento.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.model.openid;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 |
6 | /**
7 | * Memento to provide the following to OAuth authorization:
8 | *
9 | * Storage of token state
10 | *
11 | *
12 | * @since 0.0.1
13 | *
14 | */
15 | public class TokenMemento {
16 |
17 | private final String token;
18 | private final String secret;
19 | private final String rawResponse;
20 |
21 | @JsonCreator
22 | public TokenMemento(
23 | @JsonProperty("token") String token,
24 | @JsonProperty("secret") String secret,
25 | @JsonProperty("rawResponse") String rawResponse) {
26 | this.token = token;
27 | this.secret = secret;
28 | this.rawResponse = rawResponse;
29 | }
30 |
31 | public String getToken() {
32 | return token;
33 | }
34 |
35 | public String getSecret() {
36 | return secret;
37 | }
38 |
39 | public String getRawResponse() {
40 | return rawResponse;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/model/security/Authority.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.model.security;
2 |
3 | /**
4 | * Enumeration to provide the following to application:
5 | *
6 | * Provision of standard authorities grouped by Role
7 | *
8 | * An Authority exists to provide an enum key to be mapped into an RestrictedTo annotation.
9 | *
10 | * @since 0.0.1
11 | *
12 | */
13 | public enum Authority {
14 |
15 | // Naming conventions help navigation and avoid duplication
16 | // An Authority is named as VERB_SUBJECT_ENTITY
17 | // Verbs should initially follow CRUD (CREATE, RETRIEVE, UPDATE, DELETE)
18 | // Subjects are based on outward looking relationships (OWN, OTHERS)
19 | // Entities are based on primary entities (USER, CUSTOMER, CART, ITEM, INVOICE)
20 |
21 | // Roles (act as EnumSets from the fine grained authorities defined later)
22 |
23 | // Internal roles
24 | /**
25 | * The administrator role that can reach administration API functions
26 | */
27 | ROLE_ADMIN,
28 | /**
29 | * An anonymous (public) customer
30 | */
31 | ROLE_PUBLIC,
32 | /**
33 | * An un-authenticated customer in possession of a "remember me" token
34 | */
35 | ROLE_PARTIAL,
36 |
37 | // End of enum
38 | ;
39 |
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/model/security/User.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.model.security;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 | import com.fasterxml.jackson.annotation.JsonPropertyOrder;
6 | import com.google.common.base.Objects;
7 | import com.google.common.collect.Sets;
8 |
9 | import uk.co.froot.demo.openid.model.openid.DiscoveryInformationMemento;
10 |
11 | import java.io.ByteArrayInputStream;
12 | import java.io.ByteArrayOutputStream;
13 | import java.io.IOException;
14 | import java.io.ObjectInputStream;
15 | import java.io.ObjectOutputStream;
16 | import java.util.Set;
17 | import java.util.UUID;
18 |
19 | import org.brickred.socialauth.util.AccessGrant;
20 |
21 | /**
22 | *
23 | * Simple representation of a User to provide the following to Resources:
24 | *
25 | * Storage of user state
26 | *
27 | *
28 | */
29 | @JsonPropertyOrder({ "id", "userName", "passwordDigest", "firstName",
30 | "lastName", "emailAddress", "openIDDiscoveryInformationMemento",
31 | "openIDIdentifier", "sessionToken", "authorities" })
32 | public class User {
33 |
34 | /**
35 | *
36 | * Unique identifier for this entity
37 | *
38 | */
39 | private String id;
40 |
41 | /**
42 | *
43 | * A username (optional for anonymity reasons)
44 | *
45 | */
46 | private String userName;
47 |
48 | /**
49 | *
50 | * A user password (not plaintext and optional for anonymity reasons)
51 | *
52 | */
53 | @JsonProperty
54 | protected String passwordDigest = null;
55 |
56 | /**
57 | * A first name
58 | */
59 | @JsonProperty
60 | private String firstName;
61 |
62 | /**
63 | * A last name
64 | */
65 | @JsonProperty
66 | private String lastName;
67 |
68 | /**
69 | * An email address
70 | */
71 | @JsonProperty
72 | private String emailAddress;
73 |
74 | /**
75 | *
76 | * The OpenID discovery information used in phase 1 of authenticating
77 | * against an OpenID server
78 | *
79 | *
80 | * Once the OpenID identifier is in place, this can be safely deleted
81 | *
82 | */
83 | @JsonProperty
84 | private DiscoveryInformationMemento openIDDiscoveryInformationMemento;
85 |
86 | /**
87 | * An OpenID identifier that is held across sessions
88 | */
89 | @JsonProperty
90 | private String openIDIdentifier;
91 |
92 | /**
93 | * OAuth grant info in JSON format so we don't have to keep the SocialAuth
94 | * Manager alive
95 | */
96 | @JsonProperty
97 | private byte oauthGrantInfo[];
98 |
99 | /**
100 | * A shared secret between the cluster and the user's browser that is
101 | * revoked when the session ends
102 | */
103 | @JsonProperty
104 | private UUID sessionToken;
105 |
106 | /**
107 | * The authorities for this User (an unauthenticated user has no
108 | * authorities)
109 | */
110 | @JsonProperty
111 | private Set authorities = Sets.newHashSet();
112 |
113 | @JsonCreator
114 | public User(@JsonProperty("id") String id,
115 | @JsonProperty("sessionToken") UUID sessionToken) {
116 |
117 | this.id = id;
118 | this.sessionToken = sessionToken;
119 | }
120 |
121 | public String getId() {
122 | return id;
123 | }
124 |
125 | public void setId(String id) {
126 | this.id = id;
127 | }
128 |
129 | /**
130 | * @return The user name to authenticate with the client
131 | */
132 | public String getUserName() {
133 | return userName;
134 | }
135 |
136 | public void setUserName(String userName) {
137 | this.userName = userName;
138 | }
139 |
140 | /**
141 | * @return The digested password to provide authentication between the user
142 | * and the client
143 | */
144 | public String getPasswordDigest() {
145 | return passwordDigest;
146 | }
147 |
148 | /**
149 | * Note that it is expected that Jasypt or similar is used prior to
150 | * storage
151 | *
152 | * @param passwordDigest
153 | * The password digest
154 | */
155 | public void setPasswordDigest(String passwordDigest) {
156 | this.passwordDigest = passwordDigest;
157 | }
158 |
159 | public String getFirstName() {
160 | return firstName;
161 | }
162 |
163 | public void setFirstName(String firstName) {
164 | this.firstName = firstName;
165 | }
166 |
167 | public String getLastName() {
168 | return lastName;
169 | }
170 |
171 | public void setLastName(String lastName) {
172 | this.lastName = lastName;
173 | }
174 |
175 | /**
176 | * @return The user's email address
177 | */
178 | public String getEmailAddress() {
179 | return emailAddress;
180 | }
181 |
182 | public void setEmailAddress(String emailAddress) {
183 | this.emailAddress = emailAddress;
184 | }
185 |
186 | /**
187 | *
188 | * @return The OpenID discovery information (phase 1 of authentication)
189 | */
190 | public DiscoveryInformationMemento getOpenIDDiscoveryInformationMemento() {
191 | return openIDDiscoveryInformationMemento;
192 | }
193 |
194 | public void setOpenIDDiscoveryInformationMemento(
195 | DiscoveryInformationMemento openIDDiscoveryInformationMemento) {
196 | this.openIDDiscoveryInformationMemento = openIDDiscoveryInformationMemento;
197 | }
198 |
199 | /**
200 | * @return The OpenID identifier
201 | */
202 | public String getOpenIDIdentifier() {
203 | return openIDIdentifier;
204 | }
205 |
206 | public void setOpenIDIdentifier(String openIDIdentifier) {
207 | this.openIDIdentifier = openIDIdentifier;
208 | }
209 |
210 | /**
211 | * @return The session key
212 | */
213 | public UUID getSessionToken() {
214 | return sessionToken;
215 | }
216 |
217 | public void setSessionToken(UUID sessionToken) {
218 | this.sessionToken = sessionToken;
219 | }
220 |
221 | public void setAuthorities(Set authorities) {
222 | this.authorities = authorities;
223 | }
224 |
225 | public Set getAuthorities() {
226 | return authorities;
227 | }
228 |
229 | public boolean hasAllAuthorities(Set requiredAuthorities) {
230 | return authorities.containsAll(requiredAuthorities);
231 | }
232 |
233 | public boolean hasAuthority(Authority authority) {
234 | return hasAllAuthorities(Sets.newHashSet(authority));
235 | }
236 |
237 | public void setOAuthInfo(AccessGrant oathJSON) throws IOException {
238 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
239 | ObjectOutputStream oos = new ObjectOutputStream(baos);
240 | oos.writeObject(oathJSON);
241 | oauthGrantInfo = baos.toByteArray();
242 | }
243 |
244 | public AccessGrant getOAuthInfo() throws IOException,
245 | ClassNotFoundException {
246 | ByteArrayInputStream bios = new ByteArrayInputStream(oauthGrantInfo);
247 | ObjectInputStream ois = new ObjectInputStream(bios);
248 | AccessGrant grant = (AccessGrant) ois.readObject();
249 | return grant;
250 | }
251 |
252 | @Override
253 | public String toString() {
254 | return Objects.toStringHelper(this).add("userName", userName)
255 | .add("password", "**********")
256 | .add("emailAddress", emailAddress)
257 | .add("openIDIdentifier", openIDIdentifier)
258 | .add("sessionToken", sessionToken).add("firstName", firstName)
259 | .add("lastName", lastName).toString();
260 | }
261 |
262 | }
263 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/resources/BaseResource.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.resources;
2 |
3 | import java.util.Locale;
4 |
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.ws.rs.core.Context;
7 | import javax.ws.rs.core.HttpHeaders;
8 | import javax.ws.rs.core.NewCookie;
9 | import javax.ws.rs.core.UriInfo;
10 |
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | import com.google.common.base.Optional;
15 |
16 | import uk.co.froot.demo.openid.AppConfiguration;
17 | import uk.co.froot.demo.openid.model.BaseModel;
18 | import uk.co.froot.demo.openid.model.ModelBuilder;
19 | import uk.co.froot.demo.openid.model.security.User;
20 |
21 | /**
22 | * Abstract base class to provide the following to subclasses:
23 | *
24 | * Provision of common methods
25 | *
26 | *
27 | * @since 0.0.1
28 | *
29 | */
30 | public abstract class BaseResource {
31 | private static final Logger log = LoggerFactory.getLogger(BaseResource.class);
32 |
33 | protected static final String OPENID_IDENTIFIER_KEY = "openid-identifier-key";
34 |
35 | /**
36 | * Jersey creates a fresh resource every request so this is safe
37 | */
38 | @Context
39 | protected UriInfo uriInfo;
40 |
41 | /**
42 | * Jersey creates a fresh resource every request so this is safe
43 | */
44 | @Context
45 | protected HttpHeaders httpHeaders;
46 |
47 | /**
48 | * Jersey creates a fresh resource every request so this is safe
49 | */
50 | @Context
51 | protected HttpServletRequest request;
52 |
53 |
54 | protected final ModelBuilder modelBuilder = new ModelBuilder();
55 |
56 | public BaseResource() {
57 |
58 | }
59 |
60 | /**
61 | * @return The most appropriate locale for the upstream request (never null)
62 | */
63 | public Locale getLocale() {
64 | // TODO This should be a configuration setting
65 | Locale defaultLocale = Locale.UK;
66 |
67 | Locale locale;
68 | if (httpHeaders == null) {
69 | locale = defaultLocale;
70 | } else {
71 | locale = httpHeaders.getLanguage();
72 | if (locale == null) {
73 | locale = defaultLocale;
74 | }
75 | }
76 | return locale;
77 | }
78 |
79 | /**
80 | * Utility method to create a base model present on all non-authenticated resources
81 | *
82 | * @return A base model
83 | */
84 | protected BaseModel newBaseModel() {
85 |
86 | BaseModel model = modelBuilder.newBaseModel(httpHeaders);
87 |
88 | return model;
89 | }
90 |
91 |
92 | /**
93 | * @param user A user with a session token. If absent then the cookie will be removed.
94 | *
95 | * @return A cookie with a long term expiry date suitable for use as a session token for OpenID
96 | */
97 | protected NewCookie replaceSessionTokenCookie(Optional user) {
98 |
99 | if (user.isPresent()) {
100 |
101 | String value = user.get().getSessionToken().toString();
102 |
103 | log.debug("Replacing session token with {}", value);
104 |
105 | return new NewCookie(
106 | AppConfiguration.SESSION_TOKEN_NAME,
107 | value, // Value
108 | "/", // Path
109 | null, // Domain
110 | null, // Comment
111 | 86400 * 30, // 30 days
112 | false);
113 | } else {
114 | // Remove the session token cookie
115 | log.debug("Removing session token");
116 |
117 | return new NewCookie(
118 | AppConfiguration.SESSION_TOKEN_NAME,
119 | null, // Value
120 | null, // Path
121 | null, // Domain
122 | null, // Comment
123 | 0, // Expire immediately
124 | false);
125 | }
126 | }
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/resources/PrivateInfoResource.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.resources;
2 |
3 | import com.yammer.dropwizard.jersey.caching.CacheControl;
4 | import com.yammer.metrics.annotation.Timed;
5 | import uk.co.froot.demo.openid.auth.annotation.RestrictedTo;
6 | import uk.co.froot.demo.openid.model.security.Authority;
7 | import uk.co.froot.demo.openid.model.BaseModel;
8 | import uk.co.froot.demo.openid.model.security.User;
9 | import uk.co.froot.demo.openid.views.PublicFreemarkerView;
10 |
11 | import javax.ws.rs.GET;
12 | import javax.ws.rs.Path;
13 | import javax.ws.rs.Produces;
14 | import javax.ws.rs.core.MediaType;
15 |
16 | /**
17 | * Resource to provide the following to application:
18 | *
19 | * Provision of configuration for public home page
20 | *
21 | *
22 | * @since 0.0.1
23 | */
24 | @Path("/private")
25 | @Produces(MediaType.TEXT_HTML)
26 | public class PrivateInfoResource extends BaseResource {
27 |
28 | /**
29 | * @return The private home view if authenticated
30 | */
31 | @GET
32 | @Path("/home")
33 | @Timed
34 | @CacheControl(noCache = true)
35 | public PublicFreemarkerView viewHome(
36 | @RestrictedTo(Authority.ROLE_PUBLIC)
37 | User publicUser
38 | ) {
39 |
40 | BaseModel model = newBaseModel();
41 | model.loadContacts();
42 | model.loadAlbums();
43 | return new PublicFreemarkerView("private/home.ftl", model);
44 |
45 | }
46 |
47 | /**
48 | * @return The private admin view if authenticated
49 | */
50 | @GET
51 | @Path("/admin")
52 | @Timed
53 | @CacheControl(noCache = true)
54 | public PublicFreemarkerView viewAdmin(
55 | @RestrictedTo(Authority.ROLE_ADMIN)
56 | User adminUser
57 | ) {
58 |
59 | BaseModel model = newBaseModel();
60 | return new PublicFreemarkerView("private/admin.ftl", model);
61 |
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/resources/PublicErrorResource.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.resources;
2 |
3 | import com.yammer.dropwizard.jersey.caching.CacheControl;
4 | import com.yammer.metrics.annotation.Timed;
5 | import uk.co.froot.demo.openid.model.BaseModel;
6 | import uk.co.froot.demo.openid.views.PublicFreemarkerView;
7 |
8 | import javax.ws.rs.GET;
9 | import javax.ws.rs.Path;
10 | import javax.ws.rs.Produces;
11 | import javax.ws.rs.core.MediaType;
12 |
13 | /**
14 | * Resource to provide the following to application:
15 | *
16 | * Provision of error pages
17 | *
18 | *
19 | * @since 0.0.1
20 | */
21 | @Path("/error")
22 | @Produces(MediaType.TEXT_HTML)
23 | public class PublicErrorResource extends BaseResource {
24 |
25 | /**
26 | * Provide the 401 Unauthorized page
27 | *
28 | * @return A localised view containing HTML
29 | */
30 | @GET
31 | @Path("/401")
32 | @Timed
33 | @CacheControl(noCache = true)
34 | public PublicFreemarkerView view401() {
35 |
36 | // Populate the model
37 | BaseModel model = newBaseModel();
38 |
39 | return new PublicFreemarkerView("error/401.ftl",model);
40 | }
41 |
42 | /**
43 | * Provide the 404 Not Found page
44 | *
45 | * @return A localised view containing HTML
46 | */
47 | @GET
48 | @Path("/404")
49 | @Timed
50 | @CacheControl(noCache = true)
51 | public PublicFreemarkerView view404() {
52 |
53 | // Populate the model
54 | BaseModel model = newBaseModel();
55 |
56 | return new PublicFreemarkerView("error/404.ftl",model);
57 | }
58 |
59 | /**
60 | * Provide the 500 Internal Server Error page
61 | *
62 | * @return A localised view containing HTML
63 | */
64 | @GET
65 | @Path("/500")
66 | @Timed
67 | @CacheControl(noCache = true)
68 | public PublicFreemarkerView view500() {
69 |
70 | // Populate the model
71 | BaseModel model = newBaseModel();
72 |
73 | return new PublicFreemarkerView("error/500.ftl",model);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/resources/PublicHomeResource.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.resources;
2 |
3 | import java.io.InputStream;
4 | import java.net.URI;
5 | import java.net.URISyntaxException;
6 | import java.util.concurrent.TimeUnit;
7 |
8 | import javax.ws.rs.GET;
9 | import javax.ws.rs.Path;
10 | import javax.ws.rs.Produces;
11 | import javax.ws.rs.core.MediaType;
12 | import javax.ws.rs.core.Response;
13 |
14 | import uk.co.froot.demo.openid.core.InMemoryUserCache;
15 | import uk.co.froot.demo.openid.model.BaseModel;
16 | import uk.co.froot.demo.openid.model.ModelBuilder;
17 | import uk.co.froot.demo.openid.model.security.User;
18 | import uk.co.froot.demo.openid.views.PublicFreemarkerView;
19 |
20 | import com.google.common.base.Optional;
21 | import com.yammer.dropwizard.jersey.caching.CacheControl;
22 | import com.yammer.dropwizard.views.View;
23 | import com.yammer.metrics.annotation.Timed;
24 |
25 | /**
26 | * Resource to provide the following to application:
27 | *
28 | * Provision of configuration for public home page
29 | *
30 | *
31 | * @since 0.0.1
32 | */
33 | @Path("/")
34 | @Produces(MediaType.TEXT_HTML)
35 | public class PublicHomeResource extends BaseResource {
36 | //private static final Logger log = LoggerFactory.getLogger(PublicHomeResource.class);
37 |
38 | private final ModelBuilder modelBuilder = new ModelBuilder();
39 |
40 | /**
41 | * Provide the initial view on to the system
42 | *
43 | * @return A localised view containing HTML
44 | */
45 | @GET
46 | @Timed
47 | @CacheControl(noCache = true)
48 | public PublicFreemarkerView viewHome() {
49 |
50 | BaseModel model = newBaseModel();
51 | return new PublicFreemarkerView("common/home.ftl",model);
52 | }
53 |
54 | /**
55 | * Provide the initial view on to the system
56 | *
57 | * @return A localised view containing HTML
58 | */
59 | @GET
60 | @Path("markdown")
61 | @Timed
62 | @CacheControl(noCache = true)
63 | public PublicFreemarkerView viewMarkdown() {
64 |
65 | BaseModel model = newBaseModel();
66 | return new PublicFreemarkerView("common/markdown.ftl",model);
67 | }
68 |
69 | /**
70 | * Provide the initial view on to the system
71 | *
72 | * @return A the favicon images from the assets
73 | */
74 | @GET
75 | @Path("favicon.ico")
76 | @Timed
77 | @CacheControl(maxAge = 24, maxAgeUnit = TimeUnit.HOURS)
78 | public Response viewFavicon() {
79 |
80 | InputStream is = PublicHomeResource.class.getResourceAsStream("/assets/favicon.ico");
81 |
82 | return Response.ok(is).build();
83 | }
84 |
85 |
86 | /**
87 | * @return A login view with a session token
88 | */
89 | @GET
90 | @Path("login")
91 | public View login() {
92 |
93 | return new PublicFreemarkerView("common/login.ftl",
94 | modelBuilder.newBaseModel(httpHeaders));
95 | }
96 |
97 | /**
98 | * @return A login view with a session token
99 | * @throws URISyntaxException
100 | */
101 | @GET
102 | @Path("logout")
103 | public Response logout() throws URISyntaxException {
104 |
105 | BaseModel model = modelBuilder.newBaseModel(httpHeaders);
106 | User user = model.getUser();
107 | if (user != null) {
108 | // (We'll delete the user but really this would just be an update)
109 | InMemoryUserCache.INSTANCE.hardDelete(user);
110 | model.setUser(null);
111 | }
112 |
113 | // Remove the session token which will have the effect of logout
114 | return Response
115 | .temporaryRedirect(new URI("/"))
116 | .cookie(replaceSessionTokenCookie(Optional.absent()))
117 | .build();
118 |
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/resources/PublicOAuthResource.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.resources;
2 |
3 | import java.net.URI;
4 | import java.net.URISyntaxException;
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.UUID;
8 |
9 | import javax.servlet.http.HttpServletRequest;
10 | import javax.ws.rs.GET;
11 | import javax.ws.rs.Path;
12 | import javax.ws.rs.Produces;
13 | import javax.ws.rs.QueryParam;
14 | import javax.ws.rs.WebApplicationException;
15 | import javax.ws.rs.core.Context;
16 | import javax.ws.rs.core.MediaType;
17 | import javax.ws.rs.core.Response;
18 |
19 | import org.brickred.socialauth.AuthProvider;
20 | import org.brickred.socialauth.Profile;
21 | import org.brickred.socialauth.SocialAuthConfig;
22 | import org.brickred.socialauth.SocialAuthManager;
23 | import org.brickred.socialauth.util.SocialAuthUtil;
24 | import org.slf4j.Logger;
25 | import org.slf4j.LoggerFactory;
26 |
27 | import uk.co.froot.demo.openid.AppConfiguration.OAuthCfgClass;
28 | import uk.co.froot.demo.openid.OpenIDDemoService;
29 | import uk.co.froot.demo.openid.core.InMemoryUserCache;
30 | import uk.co.froot.demo.openid.model.security.Authority;
31 | import uk.co.froot.demo.openid.model.security.User;
32 |
33 | import com.google.common.base.Optional;
34 | import com.yammer.metrics.annotation.Timed;
35 |
36 | /**
37 | *
38 | * Resource to provide the following to application:
39 | *
40 | *
41 | * OAuth authentication handling
42 | *
43 | *
44 | * @since 0.0.1
45 | */
46 | @Path("/oauth")
47 | @Produces(MediaType.TEXT_HTML)
48 | public class PublicOAuthResource extends BaseResource {
49 |
50 | private static final Logger log = LoggerFactory
51 | .getLogger(PublicOAuthResource.class);
52 |
53 | /**
54 | * Default constructor
55 | */
56 | public PublicOAuthResource() {
57 | }
58 |
59 | @GET
60 | @Timed
61 | @Path("/request")
62 | public Response requestOAuth(@Context HttpServletRequest request,
63 | @QueryParam("provider") String provider) throws URISyntaxException {
64 | if (provider == null) {
65 | log.debug("Missing provider ID");
66 | throw new WebApplicationException(Response.Status.BAD_REQUEST);
67 | }
68 | // instantiate SocialAuth for this provider type and tuck into session
69 | List oauthCfg = OpenIDDemoService.getConfig()
70 | .getOAuthCfg();
71 | if (oauthCfg != null) {
72 | // get the authentication URL for this provider
73 | try {
74 | SocialAuthManager manager = getSocialAuthManager();
75 | request.getSession().setAttribute("authManager", manager);
76 |
77 | java.net.URI url = new URI(manager.getAuthenticationUrl(
78 | provider, OpenIDDemoService.getConfig()
79 | .getOAuthSuccessUrl()));
80 | log.debug("OAuth Auth URL: {}", url);
81 | return Response.temporaryRedirect(url).build();
82 | } catch (Exception e) {
83 | log.error("SocialAuth error: {}", e);
84 | }
85 | }
86 | throw new WebApplicationException(Response.Status.BAD_REQUEST);
87 | }
88 |
89 | /**
90 | * Handles the OAuth server response to the earlier AuthRequest
91 | *
92 | * @return The OAuth identifier for this user if verification was
93 | * successful
94 | */
95 | @GET
96 | @Timed
97 | @Path("/verify")
98 | public Response verifyOAuthServerResponse(
99 | @Context HttpServletRequest request) {
100 |
101 | // this was placed in the session in the /request resource
102 | SocialAuthManager manager = (SocialAuthManager) request.getSession()
103 | .getAttribute("authManager");
104 |
105 | if (manager != null) {
106 | try {
107 | // call connect method of manager which returns the provider
108 | // object
109 | Map params = SocialAuthUtil
110 | .getRequestParametersMap(request);
111 | AuthProvider provider = manager.connect(params);
112 |
113 | // get profile
114 | Profile p = provider.getUserProfile();
115 |
116 | log.info("Logging in user '{}'", p);
117 |
118 | // at this point, we've been validated, so save off this user's
119 | // info
120 | User tempUser = new User(null, UUID.randomUUID());
121 | tempUser.setOpenIDIdentifier(p.getValidatedId());
122 | tempUser.setOAuthInfo(provider.getAccessGrant());
123 |
124 | tempUser.setEmailAddress(p.getEmail());
125 | if ((p.getFirstName() == null) && (p.getLastName() == null)) {
126 | // Twitter doesn't return first/last name fields but does include
127 | // a fullname property we can use to generate them
128 | if (p.getFullName() != null) {
129 | String[] parts = p.getFullName().split("-");
130 | if (parts.length > 1) {
131 | tempUser.setFirstName(parts[0]);
132 | tempUser.setLastName(parts[parts.length-1]);
133 | } else {
134 | tempUser.setFirstName(parts[0]);
135 | tempUser.setLastName(parts[0]);
136 | }
137 | }
138 | } else {
139 | tempUser.setFirstName(p.getFirstName());
140 | tempUser.setLastName(p.getLastName());
141 | }
142 | tempUser.setUserName(p.getFullName());
143 |
144 |
145 | // Provide a basic authority in light of successful
146 | // authentication
147 | tempUser.getAuthorities().add(Authority.ROLE_PUBLIC);
148 | // see if this is an admin user (match email addr and provider)
149 | if ((OpenIDDemoService.getConfig().getAdminUsers() != null)
150 | && (tempUser.getEmailAddress() != null)) {
151 | Map adminUsers = OpenIDDemoService
152 | .getConfig().getAdminUsers();
153 | if (adminUsers.containsKey(tempUser.getEmailAddress())
154 | && (adminUsers.get(tempUser.getEmailAddress())
155 | .equals(provider.getProviderId()))) {
156 | tempUser.getAuthorities().add(Authority.ROLE_ADMIN);
157 | }
158 | }
159 |
160 | // Search for a pre-existing User matching the temp User
161 | Optional userOptional = InMemoryUserCache.INSTANCE
162 | .getByOpenIDIdentifier(tempUser.getOpenIDIdentifier());
163 | if (!userOptional.isPresent()) {
164 | // Persist the user with the generated session token
165 | InMemoryUserCache.INSTANCE.put(tempUser.getSessionToken(),
166 | tempUser);
167 |
168 | } else {
169 | tempUser = userOptional.get();
170 | }
171 |
172 | return Response
173 | .temporaryRedirect(new URI("/private/home"))
174 | .cookie(replaceSessionTokenCookie(Optional.of(tempUser)))
175 | .build();
176 | } catch (Exception e1) {
177 | log.error("Error reading profile info: {}, {}", e1.getMessage(), e1.getCause().getMessage());
178 | }
179 | }
180 |
181 | // Must have failed to be here
182 | throw new WebApplicationException(Response.Status.UNAUTHORIZED);
183 | }
184 |
185 | /**
186 | * Gets an initialized SocialAuthManager
187 | * @return gets an initialized SocialAuthManager
188 | */
189 | private SocialAuthManager getSocialAuthManager() {
190 | SocialAuthConfig config = SocialAuthConfig.getDefault();
191 | try {
192 | config.load(OpenIDDemoService.getConfig().getOAuthCfgProperties());
193 | SocialAuthManager manager = new SocialAuthManager();
194 | manager.setSocialAuthConfig(config);
195 | return manager;
196 | } catch (Exception e) {
197 | log.error("SocialAuth error: " + e);
198 | }
199 | return null;
200 | }
201 |
202 | }
203 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/resources/PublicOpenIDResource.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.resources;
2 |
3 | import java.net.MalformedURLException;
4 | import java.net.URI;
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.UUID;
8 |
9 | import javax.servlet.http.HttpServletRequest;
10 | import javax.ws.rs.FormParam;
11 | import javax.ws.rs.GET;
12 | import javax.ws.rs.POST;
13 | import javax.ws.rs.Path;
14 | import javax.ws.rs.Produces;
15 | import javax.ws.rs.QueryParam;
16 | import javax.ws.rs.WebApplicationException;
17 | import javax.ws.rs.core.Context;
18 | import javax.ws.rs.core.MediaType;
19 | import javax.ws.rs.core.Response;
20 |
21 | import org.openid4java.OpenIDException;
22 | import org.openid4java.consumer.ConsumerException;
23 | import org.openid4java.consumer.ConsumerManager;
24 | import org.openid4java.consumer.VerificationResult;
25 | import org.openid4java.discovery.DiscoveryException;
26 | import org.openid4java.discovery.DiscoveryInformation;
27 | import org.openid4java.discovery.Identifier;
28 | import org.openid4java.discovery.yadis.YadisException;
29 | import org.openid4java.message.AuthRequest;
30 | import org.openid4java.message.AuthSuccess;
31 | import org.openid4java.message.MessageException;
32 | import org.openid4java.message.ParameterList;
33 | import org.openid4java.message.ax.AxMessage;
34 | import org.openid4java.message.ax.FetchRequest;
35 | import org.openid4java.message.ax.FetchResponse;
36 | import org.openid4java.util.HttpClientFactory;
37 | import org.openid4java.util.ProxyProperties;
38 | import org.slf4j.Logger;
39 | import org.slf4j.LoggerFactory;
40 |
41 | import uk.co.froot.demo.openid.OpenIDDemoService;
42 | import uk.co.froot.demo.openid.core.InMemoryOpenIDCache;
43 | import uk.co.froot.demo.openid.core.InMemoryUserCache;
44 | import uk.co.froot.demo.openid.model.BaseModel;
45 | import uk.co.froot.demo.openid.model.openid.DiscoveryInformationMemento;
46 | import uk.co.froot.demo.openid.model.security.Authority;
47 | import uk.co.froot.demo.openid.model.security.User;
48 | import uk.co.froot.demo.openid.views.PublicFreemarkerView;
49 |
50 | import com.google.common.base.Optional;
51 | import com.yammer.dropwizard.views.View;
52 |
53 | /**
54 | * Resource to provide the following to application:
55 | *
56 | * Provision of configuration for public home page
57 | *
58 | *
59 | * @since 0.0.1
60 | */
61 | @Path("/openid")
62 | @Produces(MediaType.TEXT_HTML)
63 | public class PublicOpenIDResource extends BaseResource {
64 |
65 | private static final Logger log = LoggerFactory.getLogger(PublicOpenIDResource.class);
66 |
67 | private final static String YAHOO_ENDPOINT = "https://me.yahoo.com";
68 | private final static String GOOGLE_ENDPOINT = "https://www.google.com/accounts/o8/id";
69 |
70 | /**
71 | * Default constructor
72 | */
73 | public PublicOpenIDResource() {
74 |
75 | // Proxy configuration must come before ConsumerManager construction
76 | if (OpenIDDemoService.getConfig().getProxyHost() != null) {
77 | ProxyProperties proxyProps = new ProxyProperties();
78 | proxyProps.setProxyHostName(OpenIDDemoService.getConfig().getProxyHost());
79 | proxyProps.setProxyPort(OpenIDDemoService.getConfig().getProxyPort());
80 | HttpClientFactory.setProxyProperties(proxyProps);
81 | }
82 |
83 | }
84 |
85 | /**
86 | * Handles the authentication request from the user after they select their OpenId server
87 | *
88 | * @param identifier The identifier for the OpenId server
89 | *
90 | * @return A redirection or a form view containing user-specific permissions
91 | */
92 | @POST
93 | @SuppressWarnings("unchecked")
94 | public Response authenticationRequest(
95 | @Context
96 | HttpServletRequest request,
97 | @FormParam("identifier")
98 | String identifier
99 | ) {
100 |
101 | UUID sessionToken = UUID.randomUUID();
102 |
103 | try {
104 |
105 | // The OpenId server will use this endpoint to provide authentication
106 | // Parts of this may be shown to the user
107 | final String returnToUrl;
108 | if (request.getServerPort() == 80) {
109 | returnToUrl = String.format(
110 | "http://%s/openid/verify?token=%s",
111 | request.getServerName(),
112 | sessionToken);
113 | } else {
114 | returnToUrl = String.format(
115 | "http://%s:%d/openid/verify?token=%s",
116 | request.getServerName(),
117 | request.getServerPort(),
118 | sessionToken);
119 | }
120 |
121 | log.debug("Return to URL '{}'", returnToUrl);
122 |
123 | // Create a consumer manager for this specific request and cache it
124 | // (this is to preserve session state such as nonce values etc)
125 | ConsumerManager consumerManager = new ConsumerManager();
126 | InMemoryOpenIDCache.INSTANCE.putConsumerManager(sessionToken, consumerManager);
127 |
128 | // Perform discovery on the user-supplied identifier
129 | List> discoveries = consumerManager.discover(identifier);
130 |
131 | // Attempt to associate with the OpenID provider
132 | // and retrieve one service endpoint for authentication
133 | DiscoveryInformation discovered = consumerManager.associate(discoveries);
134 |
135 | // Create a memento to rebuild the discovered information in a subsequent request
136 | DiscoveryInformationMemento memento = new DiscoveryInformationMemento();
137 | if (discovered.getClaimedIdentifier() != null) {
138 | memento.setClaimedIdentifier(discovered.getClaimedIdentifier().getIdentifier());
139 | }
140 | memento.setDelegate(discovered.getDelegateIdentifier());
141 | if (discovered.getOPEndpoint() != null) {
142 | memento.setOpEndpoint(discovered.getOPEndpoint().toString());
143 | }
144 |
145 | memento.setTypes(discovered.getTypes());
146 | memento.setVersion(discovered.getVersion());
147 |
148 | // Create a temporary User to preserve state between requests without
149 | // using a session (we could be in a cluster)
150 | User tempUser = new User(null, sessionToken);
151 | tempUser.setOpenIDDiscoveryInformationMemento(memento);
152 | tempUser.setSessionToken(sessionToken);
153 |
154 | // Persist the User
155 | InMemoryUserCache.INSTANCE.put(sessionToken, tempUser);
156 |
157 | // Build the AuthRequest message to be sent to the OpenID provider
158 | AuthRequest authReq = consumerManager.authenticate(discovered, returnToUrl);
159 |
160 | // Build the FetchRequest containing the information to be copied
161 | // from the OpenID provider
162 | FetchRequest fetch = FetchRequest.createFetchRequest();
163 | // Attempt to decode each entry
164 | if (identifier.startsWith(GOOGLE_ENDPOINT)) {
165 | fetch.addAttribute("email", "http://axschema.org/contact/email", true);
166 | fetch.addAttribute("firstName", "http://axschema.org/namePerson/first", true);
167 | fetch.addAttribute("lastName", "http://axschema.org/namePerson/last", true);
168 | } else if (identifier.startsWith(YAHOO_ENDPOINT)) {
169 | fetch.addAttribute("email", "http://axschema.org/contact/email", true);
170 | fetch.addAttribute("fullname", "http://axschema.org/namePerson", true);
171 | } else { // works for myOpenID
172 | fetch.addAttribute("fullname", "http://schema.openid.net/namePerson", true);
173 | fetch.addAttribute("email", "http://schema.openid.net/contact/email", true);
174 | }
175 |
176 | // Attach the extension to the authentication request
177 | authReq.addExtension(fetch);
178 |
179 | // Redirect the user to their OpenId server authentication process
180 | return Response
181 | .seeOther(URI.create(authReq.getDestinationUrl(true)))
182 | .build();
183 |
184 | } catch (MessageException e1) {
185 | log.error("MessageException:", e1);
186 | } catch (YadisException e1) {
187 | log.error("YaDiscoveryException:", e1);
188 | } catch (DiscoveryException e1) {
189 | log.error("DiscoveryException:", e1);
190 | } catch (ConsumerException e1) {
191 | log.error("ConsumerException:", e1);
192 | }
193 | return Response.serverError().build();
194 | }
195 |
196 | /**
197 | * Handles the OpenId server response to the earlier AuthRequest
198 | *
199 | * @return The OpenId identifier for this user if verification was successful
200 | */
201 | @GET
202 | @Path("/verify")
203 | public Response verifyOpenIdServerResponse(
204 | @Context HttpServletRequest request,
205 | @QueryParam("token") String rawToken) {
206 |
207 | // Retrieve the previously stored discovery information from the temporary User
208 | if (rawToken == null) {
209 | log.debug("Authentication failed due to no session token");
210 | throw new WebApplicationException(Response.Status.UNAUTHORIZED);
211 | }
212 |
213 | // Build a session token from the request
214 | UUID sessionToken = UUID.fromString(rawToken);
215 |
216 | // Attempt to locate the consumer manager by the session token
217 | Optional consumerManagerOptional = InMemoryOpenIDCache.INSTANCE.getConsumerManager(sessionToken);
218 | if (!consumerManagerOptional.isPresent()) {
219 | log.debug("Authentication failed due to no consumer manager matching session token {}", rawToken);
220 | throw new WebApplicationException(Response.Status.UNAUTHORIZED);
221 | }
222 | ConsumerManager consumerManager = consumerManagerOptional.get();
223 |
224 | // Attempt to locate the user by the session token
225 | Optional tempUserOptional = InMemoryUserCache.INSTANCE.getBySessionToken(sessionToken);
226 | if (!tempUserOptional.isPresent()) {
227 | log.debug("Authentication failed due to no temp User matching session token {}", rawToken);
228 | throw new WebApplicationException(Response.Status.UNAUTHORIZED);
229 | }
230 | User tempUser = tempUserOptional.get();
231 |
232 | // Retrieve the discovery information
233 | final DiscoveryInformationMemento memento = tempUser.getOpenIDDiscoveryInformationMemento();
234 | @SuppressWarnings("serial")
235 | Identifier identifier = new Identifier() {
236 | @Override
237 | public String getIdentifier() {
238 | return memento.getClaimedIdentifier();
239 | }
240 | };
241 |
242 | DiscoveryInformation discovered;
243 | try {
244 | discovered = new DiscoveryInformation(
245 | URI.create(memento.getOpEndpoint()).toURL(),
246 | identifier,
247 | memento.getDelegate(),
248 | memento.getVersion(),
249 | memento.getTypes()
250 | );
251 | } catch (DiscoveryException e) {
252 | throw new WebApplicationException(e, Response.Status.UNAUTHORIZED);
253 | } catch (MalformedURLException e) {
254 | throw new WebApplicationException(e, Response.Status.UNAUTHORIZED);
255 | }
256 |
257 | // Extract the receiving URL from the HTTP request
258 | StringBuffer receivingURL = request.getRequestURL();
259 | String queryString = request.getQueryString();
260 | if (queryString != null && queryString.length() > 0) {
261 | receivingURL.append("?").append(request.getQueryString());
262 | }
263 | log.debug("Receiving URL = '{}", receivingURL.toString());
264 |
265 | // Extract the parameters from the authentication response
266 | // (which comes in as a HTTP request from the OpenID provider)
267 | ParameterList parameterList = new ParameterList(request.getParameterMap());
268 |
269 | try {
270 |
271 | // Verify the response
272 | // ConsumerManager needs to be the same (static) instance used
273 | // to place the authentication request
274 | // This could be tricky if this service is load-balanced
275 | VerificationResult verification = consumerManager.verify(
276 | receivingURL.toString(),
277 | parameterList,
278 | discovered);
279 |
280 | // Examine the verification result and extract the verified identifier
281 | Optional verified = Optional.fromNullable(verification.getVerifiedId());
282 | if (verified.isPresent()) {
283 | // Verified
284 | AuthSuccess authSuccess = (AuthSuccess) verification.getAuthResponse();
285 |
286 | // We have successfully authenticated so remove the temp user
287 | // and replace it with a potentially new one
288 | InMemoryUserCache.INSTANCE.hardDelete(tempUser);
289 |
290 | tempUser = new User(null, UUID.randomUUID());
291 | tempUser.setOpenIDIdentifier(verified.get().getIdentifier());
292 | tempUser.setSessionToken(sessionToken);
293 |
294 | // Extract additional information
295 | if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
296 | tempUser.setEmailAddress(extractEmailAddress(authSuccess));
297 | tempUser.setFirstName(extractFirstName(authSuccess));
298 | tempUser.setLastName(extractLastName(authSuccess));
299 | }
300 |
301 | // Provide a basic authority in light of successful authentication
302 | tempUser.getAuthorities().add(Authority.ROLE_PUBLIC);
303 | // see if this is an admin user
304 | if ((OpenIDDemoService.getConfig().getAdminUsers() != null)
305 | && (tempUser.getEmailAddress() != null)) {
306 | Map adminUsers = OpenIDDemoService
307 | .getConfig().getAdminUsers();
308 | if (adminUsers.containsKey(tempUser.getEmailAddress())
309 | && (adminUsers.get(tempUser.getEmailAddress())
310 | .equals("openid"))) {
311 | tempUser.getAuthorities().add(Authority.ROLE_ADMIN);
312 | }
313 | }
314 |
315 | log.info("Extracted a temporary {}", tempUser);
316 |
317 | // Search for a pre-existing User matching the temp User
318 | Optional userOptional = InMemoryUserCache.INSTANCE.getByOpenIDIdentifier(tempUser.getOpenIDIdentifier());
319 | User user;
320 | if (!userOptional.isPresent()) {
321 | // This is either a new registration or the OpenID identifier has changed
322 | if (tempUser.getEmailAddress() != null) {
323 | userOptional = InMemoryUserCache.INSTANCE.getByEmailAddress(tempUser.getEmailAddress());
324 | if (!userOptional.isPresent()) {
325 | // This is a new User
326 | log.debug("Registering new {}", tempUser);
327 | user = tempUser;
328 | } else {
329 | // The OpenID identifier has changed so update it
330 | log.debug("Updating OpenID identifier for {}", tempUser);
331 | user = userOptional.get();
332 | user.setOpenIDIdentifier(tempUser.getOpenIDIdentifier());
333 | }
334 |
335 | } else {
336 | // No email address to use as backup
337 | log.warn("Rejecting valid authentication. No email address for {}");
338 | throw new WebApplicationException(Response.Status.UNAUTHORIZED);
339 | }
340 | } else {
341 | // The User has been located by their OpenID identifier
342 | log.debug("Found an existing User using OpenID identifier {}", tempUser);
343 | user = userOptional.get();
344 |
345 | }
346 |
347 | // Persist the user with the current session token
348 | user.setSessionToken(sessionToken);
349 | InMemoryUserCache.INSTANCE.put(sessionToken, user);
350 |
351 | // Create a suitable view for the response
352 | // The session token has changed so we create the base model directly
353 | BaseModel model = new BaseModel();
354 | model.setUser(user);
355 |
356 | // Authenticated
357 | View view = new PublicFreemarkerView("private/home.ftl", model);
358 |
359 | // Refresh the session token cookie
360 | return Response
361 | .ok()
362 | .cookie(replaceSessionTokenCookie(Optional.of(user)))
363 | .entity(view)
364 | .build();
365 |
366 | } else {
367 | log.debug("Failed verification");
368 | }
369 | } catch (OpenIDException e) {
370 | // present error to the user
371 | log.error("OpenIDException", e);
372 | }
373 |
374 | // Must have failed to be here
375 | throw new WebApplicationException(Response.Status.UNAUTHORIZED);
376 | }
377 |
378 | private String extractEmailAddress(AuthSuccess authSuccess) throws MessageException {
379 | FetchResponse fetchResp = (FetchResponse) authSuccess.getExtension(AxMessage.OPENID_NS_AX);
380 | return getAttributeValue(
381 | fetchResp,
382 | "email",
383 | "",
384 | String.class);
385 | }
386 |
387 | private String extractFirstName(AuthSuccess authSuccess) throws MessageException {
388 | FetchResponse fetchResp = (FetchResponse) authSuccess.getExtension(AxMessage.OPENID_NS_AX);
389 | return getAttributeValue(
390 | fetchResp,
391 | "firstname",
392 | "",
393 | String.class);
394 | }
395 |
396 | private String extractLastName(AuthSuccess authSuccess) throws MessageException {
397 | FetchResponse fetchResp = (FetchResponse) authSuccess.getExtension(AxMessage.OPENID_NS_AX);
398 | return getAttributeValue(
399 | fetchResp,
400 | "lastname",
401 | "",
402 | String.class);
403 | }
404 |
405 | @SuppressWarnings({"unchecked"})
406 | private T getAttributeValue(FetchResponse fetchResponse, String attribute, T defaultValue, Class clazz) {
407 | List> list = fetchResponse.getAttributeValues(attribute);
408 | if (list != null && !list.isEmpty()) {
409 | return (T) list.get(0);
410 | }
411 |
412 | return defaultValue;
413 |
414 | }
415 |
416 | }
417 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/resources/RuntimeExceptionMapper.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.resources;
2 |
3 | import com.sun.jersey.api.core.HttpContext;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import javax.ws.rs.WebApplicationException;
8 | import javax.ws.rs.core.Context;
9 | import javax.ws.rs.core.Response;
10 | import javax.ws.rs.ext.ExceptionMapper;
11 | import javax.ws.rs.ext.Provider;
12 |
13 | /**
14 | * Provider to provide the following to Jersey framework:
15 | *
16 | * Provision of exception to response mapping
17 | *
18 | *
19 | * @since 0.0.1
20 | *
21 | */
22 | @Provider
23 | public class RuntimeExceptionMapper implements ExceptionMapper {
24 |
25 | private static final Logger log = LoggerFactory.getLogger(RuntimeExceptionMapper.class);
26 |
27 | @Context
28 | HttpContext httpContext;
29 |
30 | @Override
31 | public Response toResponse(RuntimeException runtime) {
32 |
33 | // Build default response
34 | Response defaultResponse = Response
35 | .serverError()
36 | .entity(new PublicErrorResource().view500())
37 | .build();
38 |
39 | // Check for any specific handling
40 | if (runtime instanceof WebApplicationException) {
41 |
42 | return handleWebApplicationException(runtime, defaultResponse);
43 | }
44 |
45 | // Use the default
46 | log.error(runtime.getMessage(),runtime);
47 | return defaultResponse;
48 |
49 | }
50 |
51 | private Response handleWebApplicationException(RuntimeException exception, Response defaultResponse) {
52 | WebApplicationException webAppException = (WebApplicationException) exception;
53 |
54 | // No logging
55 | if (webAppException.getResponse().getStatus() == Response.Status.UNAUTHORIZED.getStatusCode()) {
56 | return Response
57 | .status(Response.Status.UNAUTHORIZED)
58 | .entity(new PublicErrorResource().view401())
59 | .build();
60 | }
61 | if (webAppException.getResponse().getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
62 | return Response
63 | .status(Response.Status.NOT_FOUND)
64 | .entity(new PublicErrorResource().view404())
65 | .build();
66 | }
67 |
68 | // Debug logging
69 |
70 | // Warn logging
71 |
72 | // Error logging
73 | log.error(exception.getMessage(),exception);
74 |
75 | return defaultResponse;
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/froot/demo/openid/views/PublicFreemarkerView.java:
--------------------------------------------------------------------------------
1 | package uk.co.froot.demo.openid.views;
2 |
3 | import com.yammer.dropwizard.views.View;
4 | import uk.co.froot.demo.openid.model.BaseModel;
5 |
6 | /**
7 | * View to provide the following to resources:
8 | *
9 | * Representation provided by a Freemarker template with a given model
10 | *
11 | *
12 | * @since 0.0.1
13 | *
14 | */
15 | public class PublicFreemarkerView extends View {
16 |
17 | private final T model;
18 |
19 | public PublicFreemarkerView(String templateName, T model) {
20 | super("/views/ftl/"+templateName);
21 | this.model = model;
22 | }
23 |
24 | public T getModel() {
25 | return model;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/resources/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/favicon.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/images/error.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/images/error.jpg
--------------------------------------------------------------------------------
/src/main/resources/assets/images/signin-facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/images/signin-facebook.png
--------------------------------------------------------------------------------
/src/main/resources/assets/images/signin-google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/images/signin-google.png
--------------------------------------------------------------------------------
/src/main/resources/assets/images/signin-googleplus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/images/signin-googleplus.png
--------------------------------------------------------------------------------
/src/main/resources/assets/images/signin-linkedin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/images/signin-linkedin.png
--------------------------------------------------------------------------------
/src/main/resources/assets/images/signin-openid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/images/signin-openid.png
--------------------------------------------------------------------------------
/src/main/resources/assets/images/signin-twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/images/signin-twitter.png
--------------------------------------------------------------------------------
/src/main/resources/assets/images/signin-yahoo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/images/signin-yahoo.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/css/openid-shadow.css:
--------------------------------------------------------------------------------
1 | /*
2 | Simple OpenID Plugin
3 | http://code.google.com/p/openid-selector/
4 |
5 | This code is licensed under the New BSD License.
6 | */
7 |
8 | #openid_form {
9 | width: 590px;
10 | }
11 |
12 | #openid_form legend {
13 | font-weight: bold;
14 | }
15 |
16 | #openid_choice {
17 | display: none;
18 | }
19 |
20 | #openid_input_area {
21 | clear: both;
22 | padding: 10px;
23 | }
24 |
25 | #openid_btns, #openid_btns br {
26 | clear: both;
27 | }
28 |
29 | #openid_highlight {
30 | padding: 3px;
31 | background-color: #FFFCC9;
32 | float: left;
33 | }
34 |
35 | .openid_large_btn {
36 | width: 100px;
37 | height: 60px;
38 | /* fix for IE 6 only: http://en.wikipedia.org/wiki/CSS_filter#Underscore_hack */
39 | _width: 104px;
40 | _height: 64px;
41 |
42 | border: 2px solid #DDD;
43 | border-right: 2px solid #ccc;
44 | border-bottom: 2px solid #ccc;
45 | margin: 3px;
46 | float: left;
47 | border-radius: 5px;
48 | -moz-border-radius: 5px;
49 | -webkit-border-radius: 5px;
50 | box-shadow: 2px 2px 4px #ddd;
51 | -moz-box-shadow: 2px 2px 4px #ddd;
52 | -webkit-box-shadow: 2px 2px 4px #ddd;
53 | }
54 |
55 | .openid_large_btn:hover {
56 | margin: 4px 0 0 6px;
57 | border: 2px solid #999;
58 | box-shadow: none;
59 | -moz-box-shadow: none;
60 | -webkit-box-shadow: none;
61 | }
62 |
63 | .openid_small_btn {
64 | width: 24px;
65 | height: 24px;
66 | /* fix for IE 6 only: http://en.wikipedia.org/wiki/CSS_filter#Underscore_hack */
67 | _width: 28px;
68 | _height: 28px;
69 |
70 | border: 2px solid #DDD;
71 | border-right: 2px solid #ccc;
72 | border-bottom: 2px solid #ccc;
73 | margin: 3px;
74 | float: left;
75 | border-radius: 5px;
76 | -moz-border-radius: 5px;
77 | -webkit-border-radius: 5px;
78 | box-shadow: 2px 2px 4px #ddd;
79 | -moz-box-shadow: 2px 2px 4px #ddd;
80 | -webkit-box-shadow: 2px 2px 4px #ddd;
81 | }
82 |
83 | .openid_small_btn:hover {
84 | margin: 4px 0 0 6px;
85 | border: 2px solid #999;
86 | box-shadow: none;
87 | -moz-box-shadow: none;
88 | -webkit-box-shadow: none;
89 | }
90 |
91 | a.openid_large_btn:focus {
92 | outline: none;
93 | }
94 |
95 | a.openid_large_btn:focus {
96 | -moz-outline-style: none;
97 | }
98 |
99 | .openid_selected {
100 | border: 4px solid #DDD;
101 | }
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/css/openid.css:
--------------------------------------------------------------------------------
1 | /*
2 | Simple OpenID Plugin
3 | http://code.google.com/p/openid-selector/
4 |
5 | This code is licensed under the New BSD License.
6 | */
7 |
8 | #openid_form {
9 | width: 580px;
10 | }
11 |
12 | #openid_form legend {
13 | font-weight: bold;
14 | }
15 |
16 | #openid_choice {
17 | display: none;
18 | }
19 |
20 | #openid_input_area {
21 | clear: both;
22 | padding: 10px;
23 | }
24 |
25 | #openid_btns, #openid_btns br {
26 | clear: both;
27 | }
28 |
29 | #openid_highlight {
30 | padding: 3px;
31 | background-color: #FFFCC9;
32 | float: left;
33 | }
34 |
35 | .openid_large_btn {
36 | width: 100px;
37 | height: 60px;
38 | /* fix for IE 6 only: http://en.wikipedia.org/wiki/CSS_filter#Underscore_hack */
39 | _width: 102px;
40 | _height: 62px;
41 |
42 | border: 1px solid #DDD;
43 | margin: 3px;
44 | float: left;
45 | }
46 |
47 | .openid_small_btn {
48 | width: 24px;
49 | height: 24px;
50 | /* fix for IE 6 only: http://en.wikipedia.org/wiki/CSS_filter#Underscore_hack */
51 | _width: 26px;
52 | _height: 26px;
53 |
54 | border: 1px solid #DDD;
55 | margin: 3px;
56 | float: left;
57 | }
58 |
59 | a.openid_large_btn:focus {
60 | outline: none;
61 | }
62 |
63 | a.openid_large_btn:focus {
64 | -moz-outline-style: none;
65 | }
66 |
67 | .openid_selected {
68 | border: 4px solid #DDD;
69 | }
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.large/aol.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.large/aol.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.large/facebook.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.large/facebook.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.large/google.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.large/google.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.large/mailru.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.large/mailru.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.large/myopenid.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.large/myopenid.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.large/openid.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.large/openid.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.large/rambler.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.large/rambler.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.large/verisign.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.large/verisign.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.large/vkontakte.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.large/vkontakte.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.large/yahoo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.large/yahoo.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.large/yandex.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.large/yandex.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/aol.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/aol.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/aol.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/aol.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/aol.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/aol.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/blogger.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/blogger.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/blogger.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/blogger.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/blogger.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/blogger.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/claimid.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/claimid.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/claimid.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/claimid.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/claimid.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/claimid.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/clickpass.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/clickpass.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/clickpass.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/clickpass.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/clickpass.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/clickpass.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/facebook.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/facebook.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/facebook.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/facebook.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/facebook.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/facebook.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/flickr.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/flickr.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/flickr.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/flickr.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/flickr.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/flickr.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google_profile.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google_profile.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google_profile.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google_profile.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google_profile.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/google_profile.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/launchpad.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/launchpad.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/launchpad.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/launchpad.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/launchpad.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/launchpad.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/linkedin.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/linkedin.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/linkedin.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/linkedin.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/linkedin.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/linkedin.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/livejournal.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/livejournal.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/livejournal.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/livejournal.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/livejournal.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/livejournal.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/mailru.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/mailru.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/mailru.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/mailru.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/mailru.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/mailru.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/myopenid.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/myopenid.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/myopenid.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/myopenid.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/myopenid.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/myopenid.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/openid.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/openid.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/openid.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/openid.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/openid.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/openid.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/rambler.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/rambler.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/rambler.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/rambler.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/rambler.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/rambler.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/technorati.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/technorati.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/technorati.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/technorati.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/technorati.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/technorati.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/twitter.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/twitter.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/twitter.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/twitter.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/twitter.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/twitter.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/verisign.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/verisign.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/verisign.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/verisign.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/verisign.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/verisign.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vidoop.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vidoop.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vidoop.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vidoop.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vidoop.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vidoop.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vkontakte.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vkontakte.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vkontakte.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vkontakte.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vkontakte.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/vkontakte.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/winliveid.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/winliveid.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/winliveid.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/winliveid.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/winliveid.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/winliveid.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/wordpress.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/wordpress.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/wordpress.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/wordpress.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/wordpress.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/wordpress.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yahoo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yahoo.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yahoo.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yahoo.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yahoo.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yahoo.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yandex.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yandex.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yandex.ico.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yandex.ico.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yandex.ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images.small/yandex.ico.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images/openid-inputicon.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images/openid-inputicon.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images/openid-providers-en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images/openid-providers-en.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/images/openid-providers-ru.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenyee/dropwizard-oauth-openid/34e587e24fb92373ef006a83095a733ed0d516ef/src/main/resources/assets/jquery/plugins/openid-selector/images/openid-providers-ru.png
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/js/openid-de.js:
--------------------------------------------------------------------------------
1 | /*
2 | Simple OpenID Plugin
3 | http://code.google.com/p/openid-selector/
4 |
5 | This code is licensed under the New BSD License.
6 | */
7 |
8 | var providers_large = {
9 | google : {
10 | name : 'Google',
11 | url : 'https://www.google.com/accounts/o8/id'
12 | },
13 | yahoo : {
14 | name : 'Yahoo',
15 | url : 'http://me.yahoo.com/'
16 | },
17 | aol : {
18 | name : 'AOL',
19 | label : 'Bitte den AOL Benutzernamen eingeben.',
20 | url : 'http://openid.aol.com/{username}'
21 | },
22 | myopenid : {
23 | name : 'MyOpenID',
24 | label : 'Bitte den MyOpenID Benutzernamen eingeben.',
25 | url : 'http://{username}.myopenid.com/'
26 | },
27 | openid : {
28 | name : 'OpenID',
29 | label : 'Bitte OpenID eingeben.',
30 | url : null
31 | }
32 | };
33 |
34 | var providers_small = {
35 | livejournal : {
36 | name : 'LiveJournal',
37 | label : 'Bitte den LiveJournal Benutzernamen eingeben.',
38 | url : 'http://{username}.livejournal.com/'
39 | },
40 | /* flickr: {
41 | name: 'Flickr',
42 | label: 'Bitte den Flickr Benutzernamen eingeben.',
43 | url: 'http://flickr.com/{username}/'
44 | }, */
45 | /* technorati: {
46 | name: 'Technorati',
47 | label: 'Bitte den Technorati Benutzernamen eingeben.',
48 | url: 'http://technorati.com/people/technorati/{username}/'
49 | }, */
50 | wordpress : {
51 | name : 'Wordpress',
52 | label : 'Bitte den Wordpress.com Benutzernamen eingeben.',
53 | url : 'http://{username}.wordpress.com/'
54 | },
55 | blogger : {
56 | name : 'Blogger',
57 | label : 'Ihr Blogger Konto',
58 | url : 'http://{username}.blogspot.com/'
59 | },
60 | verisign : {
61 | name : 'Verisign',
62 | label : 'Ihr Verisign Benutzername',
63 | url : 'http://{username}.pip.verisignlabs.com/'
64 | },
65 | /* vidoop: {
66 | name: 'Vidoop',
67 | label: 'Ihr Vidoop Benutzername',
68 | url: 'http://{username}.myvidoop.com/'
69 | }, */
70 | /* launchpad: {
71 | name: 'Launchpad',
72 | label: 'Ihr Launchpad Benutzername',
73 | url: 'https://launchpad.net/~{username}'
74 | }, */
75 | claimid : {
76 | name : 'ClaimID',
77 | label : 'Ihr ClaimID Benutzername',
78 | url : 'http://claimid.com/{username}'
79 | },
80 | clickpass : {
81 | name : 'ClickPass',
82 | label : 'Bitte den ClickPass Benutzernamen eingeben',
83 | url : 'http://clickpass.com/public/{username}'
84 | },
85 | google_profile : {
86 | name : 'Google Profile',
87 | label : 'Bitte den Google Profile Benutzernamen eingeben',
88 | url : 'http://www.google.com/profiles/{username}'
89 | }
90 | };
91 |
92 | openid.locale = 'de';
93 | openid.sprite = 'en'; // use same sprite as english localization
94 | openid.demo_text = 'Demo Modus. Normalerweise würde die folgende OpenID übermittelt werden:';
95 | openid.signin_text = 'Anmelden';
96 | openid.image_title = 'Mit {provider} anmelden';
97 |
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/js/openid-en.js:
--------------------------------------------------------------------------------
1 | /*
2 | Simple OpenID Plugin
3 | http://code.google.com/p/openid-selector/
4 |
5 | This code is licensed under the New BSD License.
6 | */
7 |
8 | var providers_large = {
9 | google : {
10 | name : 'Google',
11 | url : 'https://www.google.com/accounts/o8/id'
12 | },
13 | yahoo : {
14 | name : 'Yahoo',
15 | url : 'http://me.yahoo.com/'
16 | },
17 | aol : {
18 | name : 'AOL',
19 | label : 'Enter your AOL screenname.',
20 | url : 'http://openid.aol.com/{username}'
21 | },
22 | myopenid : {
23 | name : 'MyOpenID',
24 | label : 'Enter your MyOpenID username.',
25 | url : 'http://{username}.myopenid.com/'
26 | },
27 | openid : {
28 | name : 'OpenID',
29 | label : 'Enter your OpenID.',
30 | url : null
31 | }
32 | };
33 |
34 | var providers_small = {
35 | livejournal : {
36 | name : 'LiveJournal',
37 | label : 'Enter your Livejournal username.',
38 | url : 'http://{username}.livejournal.com/'
39 | },
40 | /* flickr: {
41 | name: 'Flickr',
42 | label: 'Enter your Flickr username.',
43 | url: 'http://flickr.com/{username}/'
44 | }, */
45 | /* technorati: {
46 | name: 'Technorati',
47 | label: 'Enter your Technorati username.',
48 | url: 'http://technorati.com/people/technorati/{username}/'
49 | }, */
50 | wordpress : {
51 | name : 'Wordpress',
52 | label : 'Enter your Wordpress.com username.',
53 | url : 'http://{username}.wordpress.com/'
54 | },
55 | blogger : {
56 | name : 'Blogger',
57 | label : 'Your Blogger account',
58 | url : 'http://{username}.blogspot.com/'
59 | },
60 | verisign : {
61 | name : 'Verisign',
62 | label : 'Your Verisign username',
63 | url : 'http://{username}.pip.verisignlabs.com/'
64 | },
65 | /* vidoop: {
66 | name: 'Vidoop',
67 | label: 'Your Vidoop username',
68 | url: 'http://{username}.myvidoop.com/'
69 | }, */
70 | /* launchpad: {
71 | name: 'Launchpad',
72 | label: 'Your Launchpad username',
73 | url: 'https://launchpad.net/~{username}'
74 | }, */
75 | claimid : {
76 | name : 'ClaimID',
77 | label : 'Your ClaimID username',
78 | url : 'http://claimid.com/{username}'
79 | },
80 | clickpass : {
81 | name : 'ClickPass',
82 | label : 'Enter your ClickPass username',
83 | url : 'http://clickpass.com/public/{username}'
84 | },
85 | google_profile : {
86 | name : 'Google Profile',
87 | label : 'Enter your Google Profile username',
88 | url : 'http://www.google.com/profiles/{username}'
89 | }
90 | };
91 |
92 | openid.locale = 'en';
93 | openid.sprite = 'en'; // reused in german& japan localization
94 | openid.demo_text = 'In client demo mode. Normally would have submitted OpenID:';
95 | openid.signin_text = 'Sign-In';
96 | openid.image_title = 'log in with {provider}';
97 |
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/js/openid-jp.js:
--------------------------------------------------------------------------------
1 | /*
2 | Simple OpenID Plugin
3 | http://code.google.com/p/openid-selector/
4 |
5 | This code is licensed under the New BSD License.
6 | */
7 |
8 | var providers_large = {
9 | google : {
10 | name : 'Google',
11 | url : 'https://www.google.com/accounts/o8/id'
12 | },
13 | yahoo : {
14 | name : 'Yahoo',
15 | url : 'http://me.yahoo.com/'
16 | },
17 | aol : {
18 | name : 'AOL',
19 | label : 'AOLのスクリーンネームを記入してください。',
20 | url : 'http://openid.aol.com/{username}'
21 | },
22 | myopenid : {
23 | name : 'MyOpenID',
24 | label : 'MyOpenIDのユーザーネームを記入してください。',
25 | url : 'http://{username}.myopenid.com/'
26 | },
27 | openid : {
28 | name : 'OpenID',
29 | label : 'OpenIDを記入してください。',
30 | url : null
31 | }
32 | };
33 |
34 | var providers_small = {
35 | livejournal : {
36 | name : 'LiveJournal',
37 | label : 'LiveJournalのユーザーネームを記入してください。',
38 | url : 'http://{username}.livejournal.com/'
39 | },
40 | /* flickr: {
41 | name: 'Flickr',
42 | label: 'Flickrのユーザーネームを記入してください。',
43 | url: 'http://flickr.com/{username}/'
44 | }, */
45 | /* technorati: {
46 | name: 'Technorati',
47 | label: 'Technoratiのユーザーネームを記入してください。',
48 | url: 'http://technorati.com/people/technorati/{username}/'
49 | }, */
50 | wordpress : {
51 | name : 'Wordpress',
52 | label : 'Wordpress.comのユーザーネームを記入してください。',
53 | url : 'http://{username}.wordpress.com/'
54 | },
55 | blogger : {
56 | name : 'Blogger',
57 | label : 'Bloggerのアカウントを記入してください。',
58 | url : 'http://{username}.blogspot.com/'
59 | },
60 | verisign : {
61 | name : 'Verisign',
62 | label : 'Verisignのユーザーネームを記入してください。',
63 | url : 'http://{username}.pip.verisignlabs.com/'
64 | },
65 | /* vidoop: {
66 | name: 'Vidoop',
67 | label: 'Vidoopのユーザーネームを記入してください。',
68 | url: 'http://{username}.myvidoop.com/'
69 | }, */
70 | /* launchpad: {
71 | name: 'Launchpad',
72 | label: 'Launchpadのユーザーネームを記入してください。',
73 | url: 'https://launchpad.net/~{username}'
74 | }, */
75 | claimid : {
76 | name : 'ClaimID',
77 | label : 'ClaimIDのユーザーネームを記入してください。',
78 | url : 'http://claimid.com/{username}'
79 | },
80 | clickpass : {
81 | name : 'ClickPass',
82 | label : 'ClickPassのユーザーネームを記入してください。',
83 | url : 'http://clickpass.com/public/{username}'
84 | },
85 | google_profile : {
86 | name : 'Google Profile',
87 | label : 'Google Profileのユーザーネームを記入してください。',
88 | url : 'http://www.google.com/profiles/{username}'
89 | }
90 | };
91 |
92 | openid.locale = 'jp';
93 | openid.sprite = 'en'; // use same sprite as english localization
94 | openid.demo_text = '今クライアントデモモードになっています。普通は次のOpenIDを出さなければいけません:';
95 | openid.signin_text = 'ログイン';
96 | openid.image_title = '{provider}でログイン';
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/js/openid-jquery.js:
--------------------------------------------------------------------------------
1 | /*
2 | Simple OpenID Plugin
3 | http://code.google.com/p/openid-selector/
4 |
5 | This code is licensed under the New BSD License.
6 | */
7 |
8 | var providers;
9 | var openid;
10 | (function ($) {
11 | openid = {
12 | version : '1.3', // version constant
13 | demo : false,
14 | demo_text : null,
15 | cookie_expires : 6 * 30, // 6 months.
16 | cookie_name : 'openid_provider',
17 | cookie_path : '/',
18 |
19 | img_path : '/jquery/plugins/openid-selector/images/',
20 | locale : null, // is set in openid-.js
21 | sprite : null, // usually equals to locale, is set in
22 | // openid-.js
23 | signin_text : null, // text on submit button on the form
24 | all_small : false, // output large providers w/ small icons
25 | no_sprite : false, // don't use sprite image
26 | image_title : '{provider}', // for image title
27 |
28 | input_id : null,
29 | provider_url : null,
30 | provider_id : null,
31 |
32 | /**
33 | * Class constructor
34 | *
35 | * @return {Void}
36 | */
37 | init : function(input_id) {
38 | providers = $.extend({}, providers_large, providers_small);
39 | var openid_btns = $('#openid_btns');
40 | this.input_id = input_id;
41 | $('#openid_choice').show();
42 | $('#openid_input_area').empty();
43 | var i = 0;
44 | // add box for each provider
45 | for (id in providers_large) {
46 | box = this.getBoxHTML(id, providers_large[id], (this.all_small ? 'small' : 'large'), i++);
47 | openid_btns.append(box);
48 | }
49 | if (providers_small) {
50 | openid_btns.append(' ');
51 | for (id in providers_small) {
52 | box = this.getBoxHTML(id, providers_small[id], 'small', i++);
53 | openid_btns.append(box);
54 | }
55 | }
56 | $('#openid_form').submit(this.submit);
57 | var box_id = this.readCookie();
58 | if (box_id) {
59 | this.signin(box_id, true);
60 | }
61 | },
62 |
63 | /**
64 | * @return {String}
65 | */
66 | getBoxHTML : function(box_id, provider, box_size, index) {
67 | if (this.no_sprite) {
68 | var image_ext = box_size == 'small' ? '.ico.gif' : '.gif';
69 | return ' ';
72 | }
73 | var x = box_size == 'small' ? -index * 24 : -index * 100;
74 | var y = box_size == 'small' ? -60 : 0;
75 | return ' ';
78 | },
79 |
80 | /**
81 | * Provider image click
82 | *
83 | * @return {Void}
84 | */
85 | signin : function(box_id, onload) {
86 | var provider = providers[box_id];
87 | if (!provider) {
88 | return;
89 | }
90 | this.highlight(box_id);
91 | this.setCookie(box_id);
92 | this.provider_id = box_id;
93 | this.provider_url = provider['url'];
94 | // prompt user for input?
95 | if (provider['label']) {
96 | this.useInputBox(provider);
97 | } else {
98 | $('#openid_input_area').empty();
99 | if (!onload) {
100 | $('#openid_form').submit();
101 | }
102 | }
103 | },
104 |
105 | /**
106 | * Sign-in button click
107 | *
108 | * @return {Boolean}
109 | */
110 | submit : function() {
111 | var url = openid.provider_url;
112 | if (url) {
113 | url = url.replace('{username}', $('#openid_username').val());
114 | openid.setOpenIdUrl(url);
115 | }
116 | if (openid.demo) {
117 | alert(openid.demo_text + "\r\n" + document.getElementById(openid.input_id).value);
118 | return false;
119 | }
120 | if (url.indexOf("javascript:") == 0) {
121 | url = url.substr("javascript:".length);
122 | eval(url);
123 | return false;
124 | }
125 | return true;
126 | },
127 |
128 | /**
129 | * @return {Void}
130 | */
131 | setOpenIdUrl : function(url) {
132 | var hidden = document.getElementById(this.input_id);
133 | if (hidden != null) {
134 | hidden.value = url;
135 | } else {
136 | $('#openid_form').append(' ');
137 | }
138 | },
139 |
140 | /**
141 | * @return {Void}
142 | */
143 | highlight : function(box_id) {
144 | // remove previous highlight.
145 | var highlight = $('#openid_highlight');
146 | if (highlight) {
147 | highlight.replaceWith($('#openid_highlight a')[0]);
148 | }
149 | // add new highlight.
150 | $('.' + box_id).wrap('
');
151 | },
152 |
153 | setCookie : function(value) {
154 | var date = new Date();
155 | date.setTime(date.getTime() + (this.cookie_expires * 24 * 60 * 60 * 1000));
156 | var expires = "; expires=" + date.toGMTString();
157 | document.cookie = this.cookie_name + "=" + value + expires + "; path=" + this.cookie_path;
158 | },
159 |
160 | readCookie : function() {
161 | var nameEQ = this.cookie_name + "=";
162 | var ca = document.cookie.split(';');
163 | for ( var i = 0; i < ca.length; i++) {
164 | var c = ca[i];
165 | while (c.charAt(0) == ' ')
166 | c = c.substring(1, c.length);
167 | if (c.indexOf(nameEQ) == 0)
168 | return c.substring(nameEQ.length, c.length);
169 | }
170 | return null;
171 | },
172 |
173 | /**
174 | * @return {Void}
175 | */
176 | useInputBox : function(provider) {
177 | var input_area = $('#openid_input_area');
178 | var html = '';
179 | var id = 'openid_username';
180 | var value = '';
181 | var label = provider['label'];
182 | var style = '';
183 | if (label) {
184 | html = '' + label + '
';
185 | }
186 | if (provider['name'] == 'OpenID') {
187 | id = this.input_id;
188 | value = 'http://';
189 | style = 'background: #FFF url(' + this.img_path + 'openid-inputicon.gif) no-repeat scroll 0 50%; padding-left:18px;';
190 | }
191 | html += ' '
192 | + ' ';
193 | input_area.empty();
194 | input_area.append(html);
195 | $('#' + id).focus();
196 | },
197 |
198 | setDemoMode : function(demoMode) {
199 | this.demo = demoMode;
200 | }
201 | };
202 | })(jQuery);
203 |
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/js/openid-ru.js:
--------------------------------------------------------------------------------
1 | /*
2 | Simple OpenID Plugin
3 | http://code.google.com/p/openid-selector/
4 |
5 | This code is licensed under the New BSD License.
6 | */
7 |
8 | var providers_large = {
9 | yandex : {
10 | name : 'Яндекс',
11 | url : 'http://openid.yandex.ru'
12 | },
13 | rambler : {
14 | name : 'Рамблер',
15 | url : 'http://www.rambler.ru'
16 | },
17 | google : {
18 | name : 'Google',
19 | url : 'https://www.google.com/accounts/o8/id'
20 | },
21 | yahoo : {
22 | name : 'Yahoo',
23 | url : 'http://me.yahoo.com/'
24 | },
25 | myopenid : {
26 | name : 'MyOpenID',
27 | label : 'Введите ваше имя пользователя на MyOpenID.',
28 | url : 'http://{username}.myopenid.com/'
29 | }
30 | };
31 |
32 | var providers_small = {
33 | openid : {
34 | name : 'OpenID',
35 | label : 'Введите ваш OpenID.',
36 | url : null
37 | },
38 | livejournal : {
39 | name : 'Живой Журнал',
40 | label : 'Введите ваше имя в Живом Журнале.',
41 | url : 'http://{username}.livejournal.com/'
42 | },
43 | flickr : {
44 | name : 'Flickr',
45 | label : 'Введите ваше имя на Flickr.',
46 | url : 'http://flickr.com/{username}/'
47 | },
48 | wordpress : {
49 | name : 'Wordpress',
50 | label : 'Введите ваше имя на Wordpress.com.',
51 | url : 'http://{username}.wordpress.com/'
52 | },
53 | blogger : {
54 | name : 'Blogger',
55 | label : 'Ваш Blogger аккаунт',
56 | url : 'http://{username}.blogspot.com/'
57 | },
58 | verisign : {
59 | name : 'Verisign',
60 | label : 'Ваше имя пользователя на Verisign',
61 | url : 'http://{username}.pip.verisignlabs.com/'
62 | },
63 | google_profile : {
64 | name : 'Профиль Google',
65 | label : 'Введите ваше имя на Google Profile',
66 | url : 'http://www.google.com/profiles/{username}'
67 | }
68 | };
69 |
70 | openid.locale = 'ru';
71 | openid.sprite = 'ru'; // reused in ukrainian localization
72 | openid.demo_text = 'В демонстрационном режиме на клиенте. В действительности произошел бы сабмит следующего OpenID:';
73 | openid.signin_text = 'Войти';
74 | openid.image_title = 'войти c {provider}';
75 |
--------------------------------------------------------------------------------
/src/main/resources/assets/jquery/plugins/openid-selector/js/openid-uk.js:
--------------------------------------------------------------------------------
1 | /*
2 | Simple OpenID Plugin
3 | http://code.google.com/p/openid-selector/
4 |
5 | This code is licensed under the New BSD License.
6 | */
7 |
8 | var providers_large = {
9 | yandex : {
10 | name : 'Яндекс',
11 | url : 'http://openid.yandex.ru'
12 | },
13 | rambler : {
14 | name : 'Рамблер',
15 | url : 'http://www.rambler.ru'
16 | },
17 | google : {
18 | name : 'Google',
19 | url : 'https://www.google.com/accounts/o8/id'
20 | },
21 | yahoo : {
22 | name : 'Yahoo',
23 | url : 'http://me.yahoo.com/'
24 | },
25 | myopenid : {
26 | name : 'MyOpenID',
27 | label : 'Введіть ваше ім\'я користувача на MyOpenID.',
28 | url : 'http://{username}.myopenid.com/'
29 | }
30 | };
31 |
32 | var providers_small = {
33 | openid : {
34 | name : 'OpenID',
35 | label : 'Введіть ваш OpenID.',
36 | url : null
37 | },
38 | livejournal : {
39 | name : 'Живий Журнал',
40 | label : 'Введіть ваше ім\'я в Живому Журналі.',
41 | url : 'http://{username}.livejournal.com/'
42 | },
43 | flickr : {
44 | name : 'Flickr',
45 | label : 'Введіть ваше ім\'я на Flickr.',
46 | url : 'http://flickr.com/{username}/'
47 | },
48 | wordpress : {
49 | name : 'Wordpress',
50 | label : 'Введіть ваше ім\'я на Wordpress.com.',
51 | url : 'http://{username}.wordpress.com/'
52 | },
53 | blogger : {
54 | name : 'Blogger',
55 | label : 'Ваш Blogger аккаунт',
56 | url : 'http://{username}.blogspot.com/'
57 | },
58 | verisign : {
59 | name : 'Verisign',
60 | label : 'Ваше ім\'я користувача на Verisign',
61 | url : 'http://{username}.pip.verisignlabs.com/'
62 | },
63 | google_profile : {
64 | name : 'Профіль Google',
65 | label : 'Введіть ваше ім\'я на Google Profile',
66 | url : 'http://www.google.com/profiles/{username}'
67 | }
68 | };
69 |
70 | openid.locale = 'uk';
71 | openid.sprite = 'ru'; // use same sprite as russian localization
72 | openid.demo_text = 'В демонстраційному режимі на клієнті. Насправді пройшов би сабміт наступного OpenID:';
73 | openid.signin_text = 'Увійти';
74 | openid.image_title = 'увійти з {provider}';
75 |
--------------------------------------------------------------------------------
/src/main/resources/banner.txt:
--------------------------------------------------------------------------------
1 | OAuth/OpenID Demo - Powered by Dropwizard
2 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/common/home.ftl:
--------------------------------------------------------------------------------
1 | <#-- @ftlvariable name="" type="uk.co.froot.demo.openid.views.PublicFreemarkerView" -->
2 |
3 |
4 |
5 | <#include "../includes/common/head.ftl">
6 |
7 |
8 |
9 |
10 | <#include "../includes/common/header.ftl">
11 |
12 |
Public home page
13 |
14 |
Anyone can access this page
15 |
16 |
Access protected info . This is available to anyone after authentication
17 |
18 |
Access private info . This is only available to people who authenticate with the
19 | specific email address set in app.yml as adminUsers.
20 |
21 |
22 |
23 |
Show Markdown demo text . This is available to anyone.
24 |
25 | <#include "../includes/common/footer.ftl">
26 |
27 |
28 |
29 | <#include "../includes/common/cdn-scripts.ftl">
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/common/login.ftl:
--------------------------------------------------------------------------------
1 | <#-- @ftlvariable name="" type="uk.co.froot.demo.openid.views.PublicFreemarkerView" -->
2 |
3 |
4 |
5 | <#include "../includes/common/head.ftl">
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | <#include "../includes/common/header.ftl">
15 |
16 |
JQuery Simple OpenID Selector Demo
17 |
18 |
This is a simple example to show how you can include the Javascript into your page.
19 |
20 |
This example is based on the openid-selector project that
21 | also supports Prototype and Mootools.
22 |
23 |
24 |
43 |
44 |
45 |
46 |
OAuth Login Buttons
47 |
48 |
These buttons log you in via 3rd party OAuth services.
49 |
50 |
We need to request the email scopes from each service so we can use that
51 | for authorization. But we can also request access to other stuff like
52 | your feeds or photos.
53 |
54 |
61 |
62 |
63 |
64 |
65 | <#include "../includes/common/footer.ftl">
66 |
67 |
68 |
69 | <#include "../includes/common/cdn-scripts.ftl">
70 |
71 |
72 |
73 |
74 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/common/markdown.ftl:
--------------------------------------------------------------------------------
1 | <#-- @ftlvariable name="" type="uk.co.froot.demo.openid.views.PublicFreemarkerView" -->
2 |
3 |
4 |
5 | <#include "../includes/common/head.ftl">
6 |
7 |
8 |
9 |
10 | <#include "../includes/common/header.ftl">
11 |
12 |
13 | ${model.markdownHtml}
14 |
15 | <#include "../includes/common/footer.ftl">
16 |
17 |
18 |
19 | <#include "../includes/common/cdn-scripts.ftl">
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/error/401.ftl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <#include "../includes/common/head.ftl">
5 |
6 |
7 |
8 |
9 |
21 |
22 |
23 | <#include "../includes/common/cdn-scripts.ftl">
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/error/404.ftl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <#include "../includes/common/head.ftl">
5 |
6 |
7 |
8 |
9 |
10 | <#include "../includes/common/header.ftl">
11 |
12 |
13 |
14 |
There doesn't appear to be anything here
15 |
16 |
17 |
It looks as though you were following a broken link. If it came from our site, we're very sorry about
18 | this and our link maintenance reporting tool will have picked this up and
19 | sent a notification to the support engineers about it. They will sort it out as soon as possible.
20 |
21 |
Since the information isn't here, you should return to the home page .
22 |
23 |
Technical
24 |
404 - Resource not found
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | <#include "../includes/common/footer.ftl">
39 |
40 |
41 |
42 |
43 | <#include "../includes/common/cdn-scripts.ftl">
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/error/500.ftl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <#include "../includes/common/head.ftl">
5 |
6 |
7 |
8 |
9 |
10 | <#include "../includes/common/header.ftl">
11 |
12 |
13 |
14 |
Oh, snap!
15 |
It looks as though something has broken on our system. Our internal error reporting system has picked this up
16 | and has sent notification to the support engineers about it. They will sort it out as soon as possible.
17 |
18 |
We are very sorry to cause you this inconvenience, your time is precious and we screwed up.
19 |
20 |
Since there isn't much else to done, you should return to the home page .
21 |
22 |
Technical
23 |
500 - Internal server error
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | <#include "../includes/common/footer.ftl">
37 |
38 |
39 |
40 |
41 | <#include "../includes/common/cdn-scripts.ftl">
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/includes/common/cdn-scripts.ftl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/includes/common/footer.ftl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/includes/common/head.ftl:
--------------------------------------------------------------------------------
1 |
2 | OpenId/OAuth Demonstrator
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/includes/common/header.ftl:
--------------------------------------------------------------------------------
1 | <#-- @ftlvariable name="" type="uk.co.froot.demo.dojo.views.PublicFreemarkerView" -->
2 |
3 |
4 |
15 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/includes/common/left-sidebar.ftl:
--------------------------------------------------------------------------------
1 |
Left sidebar
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/includes/common/right-sidebar.ftl:
--------------------------------------------------------------------------------
1 |
Right sidebar
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/private/admin.ftl:
--------------------------------------------------------------------------------
1 | <#-- @ftlvariable name="" type="uk.co.froot.demo.openid.views.PublicFreemarkerView" -->
2 |
3 |
4 |
5 | <#include "../includes/common/head.ftl">
6 |
7 |
8 |
9 |
10 | <#include "../includes/common/header.ftl">
11 |
12 |
Admin data
13 |
This can only be seen by administrators
14 |
15 | <#include "../includes/common/footer.ftl">
16 |
17 |
18 |
19 | <#include "../includes/common/cdn-scripts.ftl">
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/main/resources/views/ftl/private/home.ftl:
--------------------------------------------------------------------------------
1 | <#-- @ftlvariable name="" type="uk.co.froot.demo.openid.views.PublicFreemarkerView" -->
2 |
3 |
4 |
5 | <#include "../includes/common/head.ftl">
6 |
7 |
8 |
9 |
10 | <#include "../includes/common/header.ftl">
11 |
12 |
13 |
Your private data
14 |
Congratulations! You authenticated through OpenId or OAuth
15 |
This can be seen by administrators and authenticated public
16 |
Try to get to the admin page
17 |
18 |
19 | <#if model.contacts??>
20 |
21 |
Social Contacts
22 | <#list model.contacts as c>
23 |
32 | #list>
33 |
34 | #if>
35 |
36 | <#if model.albums??>
37 |
38 |
Albums
39 | <#list model.albums as a>
40 |
56 | #list>
57 |
58 | #if>
59 |
60 | <#include "../includes/common/footer.ftl">
61 |
62 |
63 |
64 | <#include "../includes/common/cdn-scripts.ftl">
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/main/resources/views/markdown/demo-all-elements.md:
--------------------------------------------------------------------------------
1 | Markdown: Syntax
2 | ================
3 |
4 | This text is lifted from [Daring Fireballs's syntax page](http://daringfireball.net/projects/markdown/syntax) to promote the use of Markdown.
5 |
6 |
13 |
14 | * [Overview](#overview)
15 | * [Philosophy](#philosophy)
16 | * [Inline HTML (tables and divs)](#html)
17 | * [Automatic Escaping for Special Characters](#autoescape)
18 | * [Block Elements](#block)
19 | * [Paragraphs and Line Breaks](#p)
20 | * [Headers](#header)
21 | * [Blockquotes](#blockquote)
22 | * [Lists](#list)
23 | * [Code Blocks](#precode)
24 | * [Horizontal Rules](#hr)
25 | * [Span Elements](#span)
26 | * [Links](#link)
27 | * [Emphasis](#em)
28 | * [Code](#code)
29 | * [Images](#img)
30 | * [Miscellaneous](#misc)
31 | * [Backslash Escapes](#backslash)
32 | * [Automatic Links](#autolink)
33 |
34 |
35 | **Note:** This document is itself written using Markdown; you
36 | can [see the source for it by adding '.text' to the URL][src].
37 |
38 | [src]: /projects/markdown/syntax.text
39 |
40 | * * *
41 |
42 |
Overview
43 |
44 |
Philosophy
45 |
46 | Markdown is intended to be as easy-to-read and easy-to-write as is feasible.
47 |
48 | Readability, however, is emphasized above all else. A Markdown-formatted
49 | document should be publishable as-is, as plain text, without looking
50 | like it's been marked up with tags or formatting instructions. While
51 | Markdown's syntax has been influenced by several existing text-to-HTML
52 | filters -- including [Setext] [1], [atx] [2], [Textile] [3], [reStructuredText] [4],
53 | [Grutatext] [5], and [EtText] [6] -- the single biggest source of
54 | inspiration for Markdown's syntax is the format of plain text email.
55 |
56 | [1]: http://docutils.sourceforge.net/mirror/setext.html
57 | [2]: http://www.aaronsw.com/2002/atx/
58 | [3]: http://textism.com/tools/textile/
59 | [4]: http://docutils.sourceforge.net/rst.html
60 | [5]: http://www.triptico.com/software/grutatxt.html
61 | [6]: http://ettext.taint.org/doc/
62 |
63 | To this end, Markdown's syntax is comprised entirely of punctuation
64 | characters, which punctuation characters have been carefully chosen so
65 | as to look like what they mean. E.g., asterisks around a word actually
66 | look like \*emphasis\*. Markdown lists look like, well, lists. Even
67 | blockquotes look like quoted passages of text, assuming you've ever
68 | used email.
69 |
70 |
71 |
72 |
Inline HTML (tables, divs etc)
73 |
74 | Markdown's syntax is intended for one purpose: to be used as a
75 | format for *writing* for the web.
76 |
77 | Markdown is not a replacement for HTML, or even close to it. Its
78 | syntax is very small, corresponding only to a very small subset of
79 | HTML tags. The idea is *not* to create a syntax that makes it easier
80 | to insert HTML tags. In my opinion, HTML tags are already easy to
81 | insert. The idea for Markdown is to make it easy to read, write, and
82 | edit prose. HTML is a *publishing* format; Markdown is a *writing*
83 | format. Thus, Markdown's formatting syntax only addresses issues that
84 | can be conveyed in plain text.
85 |
86 | For any markup that is not covered by Markdown's syntax, you simply
87 | use HTML itself. There's no need to preface it or delimit it to
88 | indicate that you're switching from Markdown to HTML; you just use
89 | the tags.
90 |
91 | The only restrictions are that block-level HTML elements -- e.g. `
`,
92 | `
`, ``, ``, etc. -- must be separated from surrounding
93 | content by blank lines, and the start and end tags of the block should
94 | not be indented with tabs or spaces. Markdown is smart enough not
95 | to add extra (unwanted) `
` tags around HTML block-level tags.
96 |
97 | For example, to add an HTML table to a Markdown article:
98 |
99 | This is a regular paragraph.
100 |
101 |
102 |
103 | Foo
104 | Bar
105 |
106 |
107 |
108 | This is another regular paragraph.
109 |
110 | Rendered:
111 |
112 |
113 | Foo
114 | Bar
115 |
116 |
117 |
118 | Note that Markdown formatting syntax is not processed within block-level
119 | HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an
120 | HTML block.
121 |
122 | Span-level HTML tags -- e.g. ``, ``, or `` -- can be
123 | used anywhere in a Markdown paragraph, list item, or header. If you
124 | want, you can even use HTML tags instead of Markdown formatting; e.g. if
125 | you'd prefer to use HTML `` or ` ` tags instead of Markdown's
126 | link or image syntax, go right ahead.
127 |
128 | Unlike block-level HTML tags, Markdown syntax *is* processed within
129 | span-level tags.
130 |
131 |
132 | Automatic Escaping for Special Characters
133 |
134 | In HTML, there are two characters that demand special treatment: `<`
135 | and `&`. Left angle brackets are used to start tags; ampersands are
136 | used to denote HTML entities. If you want to use them as literal
137 | characters, you must escape them as entities, e.g. `<`, and
138 | `&`.
139 |
140 | Ampersands in particular are bedeviling for web writers. If you want to
141 | write about 'AT&T', you need to write '`AT&T`'. You even need to
142 | escape ampersands within URLs. Thus, if you want to link to:
143 |
144 | http://images.google.com/images?num=30&q=larry+bird
145 |
146 | you need to encode the URL as:
147 |
148 | http://images.google.com/images?num=30&q=larry+bird
149 |
150 | in your anchor tag `href` attribute. Needless to say, this is easy to
151 | forget, and is probably the single most common source of HTML validation
152 | errors in otherwise well-marked-up web sites.
153 |
154 | Markdown allows you to use these characters naturally, taking care of
155 | all the necessary escaping for you. If you use an ampersand as part of
156 | an HTML entity, it remains unchanged; otherwise it will be translated
157 | into `&`.
158 |
159 | So, if you want to include a copyright symbol in your article, you can write:
160 |
161 | ©
162 |
163 | and Markdown will leave it alone. But if you write:
164 |
165 | AT&T
166 |
167 | Markdown will translate it to:
168 |
169 | AT&T
170 |
171 | Similarly, because Markdown supports [inline HTML](#html), if you use
172 | angle brackets as delimiters for HTML tags, Markdown will treat them as
173 | such. But if you write:
174 |
175 | 4 < 5
176 |
177 | Markdown will translate it to:
178 |
179 | 4 < 5
180 |
181 | However, inside Markdown code spans and blocks, angle brackets and
182 | ampersands are *always* encoded automatically. This makes it easy to use
183 | Markdown to write about HTML code. (As opposed to raw HTML, which is a
184 | terrible format for writing about HTML syntax, because every single `<`
185 | and `&` in your example code needs to be escaped.)
186 |
187 |
188 | * * *
189 |
190 |
191 | Block Elements
192 |
193 |
194 | Paragraphs and Line Breaks
195 |
196 | A paragraph is simply one or more consecutive lines of text, separated
197 | by one or more blank lines. (A blank line is any line that looks like a
198 | blank line -- a line containing nothing but spaces or tabs is considered
199 | blank.) Normal paragraphs should not be indented with spaces or tabs.
200 |
201 | The implication of the "one or more consecutive lines of text" rule is
202 | that Markdown supports "hard-wrapped" text paragraphs. This differs
203 | significantly from most other text-to-HTML formatters (including Movable
204 | Type's "Convert Line Breaks" option) which translate every line break
205 | character in a paragraph into a ` ` tag.
206 |
207 | When you *do* want to insert a ` ` break tag using Markdown, you
208 | end a line with two or more spaces, then type return.
209 |
210 | Yes, this takes a tad more effort to create a ` `, but a simplistic
211 | "every line break is a ` `" rule wouldn't work for Markdown.
212 | Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l]
213 | work best -- and look better -- when you format them with hard breaks.
214 |
215 | [bq]: #blockquote
216 | [l]: #list
217 |
218 |
219 |
220 |
221 |
222 | Markdown supports two styles of headers, [Setext] [1] and [atx] [2].
223 |
224 | Setext-style headers are "underlined" using equal signs (for first-level
225 | headers) and dashes (for second-level headers). For example:
226 |
227 | This is an H1
228 | =============
229 |
230 | This is an H2
231 | -------------
232 |
233 | Any number of underlining `=`'s or `-`'s will work.
234 |
235 | Atx-style headers use 1-6 hash characters at the start of the line,
236 | corresponding to header levels 1-6. For example:
237 |
238 | # This is an H1
239 |
240 | ## This is an H2
241 |
242 | ###### This is an H6
243 |
244 | Optionally, you may "close" atx-style headers. This is purely
245 | cosmetic -- you can use this if you think it looks better. The
246 | closing hashes don't even need to match the number of hashes
247 | used to open the header. (The number of opening hashes
248 | determines the header level.) :
249 |
250 | # This is an H1 #
251 |
252 | ## This is an H2 ##
253 |
254 | ### This is an H3 ######
255 |
256 |
257 | Blockquotes
258 |
259 | Markdown uses email-style `>` characters for blockquoting. If you're
260 | familiar with quoting passages of text in an email message, then you
261 | know how to create a blockquote in Markdown. It looks best if you hard
262 | wrap the text and put a `>` before every line:
263 |
264 | > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
265 | > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
266 | > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
267 | >
268 | > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
269 | > id sem consectetuer libero luctus adipiscing.
270 |
271 | Markdown allows you to be lazy and only put the `>` before the first
272 | line of a hard-wrapped paragraph:
273 |
274 | > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
275 | consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
276 | Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
277 |
278 | > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
279 | id sem consectetuer libero luctus adipiscing.
280 |
281 | Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by
282 | adding additional levels of `>`:
283 |
284 | > This is the first level of quoting.
285 | >
286 | > > This is nested blockquote.
287 | >
288 | > Back to the first level.
289 |
290 | Blockquotes can contain other Markdown elements, including headers, lists,
291 | and code blocks:
292 |
293 | > ## This is a header.
294 | >
295 | > 1. This is the first list item.
296 | > 2. This is the second list item.
297 | >
298 | > Here's some example code:
299 | >
300 | > return shell_exec("echo $input | $markdown_script");
301 |
302 | Any decent text editor should make email-style quoting easy. For
303 | example, with BBEdit, you can make a selection and choose Increase
304 | Quote Level from the Text menu.
305 |
306 |
307 | Lists
308 |
309 | Markdown supports ordered (numbered) and unordered (bulleted) lists.
310 |
311 | Unordered lists use asterisks, pluses, and hyphens -- interchangably
312 | -- as list markers:
313 |
314 | * Red
315 | * Green
316 | * Blue
317 |
318 | is equivalent to:
319 |
320 | + Red
321 | + Green
322 | + Blue
323 |
324 | and:
325 |
326 | - Red
327 | - Green
328 | - Blue
329 |
330 | Ordered lists use numbers followed by periods:
331 |
332 | 1. Bird
333 | 2. McHale
334 | 3. Parish
335 |
336 | It's important to note that the actual numbers you use to mark the
337 | list have no effect on the HTML output Markdown produces. The HTML
338 | Markdown produces from the above list is:
339 |
340 |
341 | Bird
342 | McHale
343 | Parish
344 |
345 |
346 | If you instead wrote the list in Markdown like this:
347 |
348 | 1. Bird
349 | 1. McHale
350 | 1. Parish
351 |
352 | or even:
353 |
354 | 3. Bird
355 | 1. McHale
356 | 8. Parish
357 |
358 | you'd get the exact same HTML output. The point is, if you want to,
359 | you can use ordinal numbers in your ordered Markdown lists, so that
360 | the numbers in your source match the numbers in your published HTML.
361 | But if you want to be lazy, you don't have to.
362 |
363 | If you do use lazy list numbering, however, you should still start the
364 | list with the number 1. At some point in the future, Markdown may support
365 | starting ordered lists at an arbitrary number.
366 |
367 | List markers typically start at the left margin, but may be indented by
368 | up to three spaces. List markers must be followed by one or more spaces
369 | or a tab.
370 |
371 | To make lists look nice, you can wrap items with hanging indents:
372 |
373 | * Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
374 | Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
375 | viverra nec, fringilla in, laoreet vitae, risus.
376 | * Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
377 | Suspendisse id sem consectetuer libero luctus adipiscing.
378 |
379 | But if you want to be lazy, you don't have to:
380 |
381 | * Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
382 | Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
383 | viverra nec, fringilla in, laoreet vitae, risus.
384 | * Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
385 | Suspendisse id sem consectetuer libero luctus adipiscing.
386 |
387 | If list items are separated by blank lines, Markdown will wrap the
388 | items in `` tags in the HTML output. For example, this input:
389 |
390 | * Bird
391 | * Magic
392 |
393 | will turn into:
394 |
395 |
396 | Bird
397 | Magic
398 |
399 |
400 | But this:
401 |
402 | * Bird
403 |
404 | * Magic
405 |
406 | will turn into:
407 |
408 |
409 | Bird
410 | Magic
411 |
412 |
413 | List items may consist of multiple paragraphs. Each subsequent
414 | paragraph in a list item must be indented by either 4 spaces
415 | or one tab:
416 |
417 | 1. This is a list item with two paragraphs. Lorem ipsum dolor
418 | sit amet, consectetuer adipiscing elit. Aliquam hendrerit
419 | mi posuere lectus.
420 |
421 | Vestibulum enim wisi, viverra nec, fringilla in, laoreet
422 | vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
423 | sit amet velit.
424 |
425 | 2. Suspendisse id sem consectetuer libero luctus adipiscing.
426 |
427 | It looks nice if you indent every line of the subsequent
428 | paragraphs, but here again, Markdown will allow you to be
429 | lazy:
430 |
431 | * This is a list item with two paragraphs.
432 |
433 | This is the second paragraph in the list item. You're
434 | only required to indent the first line. Lorem ipsum dolor
435 | sit amet, consectetuer adipiscing elit.
436 |
437 | * Another item in the same list.
438 |
439 | To put a blockquote within a list item, the blockquote's `>`
440 | delimiters need to be indented:
441 |
442 | * A list item with a blockquote:
443 |
444 | > This is a blockquote
445 | > inside a list item.
446 |
447 | To put a code block within a list item, the code block needs
448 | to be indented *twice* -- 8 spaces or two tabs:
449 |
450 | * A list item with a code block:
451 |
452 |
453 |
454 |
455 | It's worth noting that it's possible to trigger an ordered list by
456 | accident, by writing something like this:
457 |
458 | 1986. What a great season.
459 |
460 | In other words, a *number-period-space* sequence at the beginning of a
461 | line. To avoid this, you can backslash-escape the period:
462 |
463 | 1986\. What a great season.
464 |
465 |
466 |
467 | Code Blocks
468 |
469 | Pre-formatted code blocks are used for writing about programming or
470 | markup source code. Rather than forming normal paragraphs, the lines
471 | of a code block are interpreted literally. Markdown wraps a code block
472 | in both `` and `` tags.
473 |
474 | To produce a code block in Markdown, simply indent every line of the
475 | block by at least 4 spaces or 1 tab. For example, given this input:
476 |
477 | This is a normal paragraph:
478 |
479 | This is a code block.
480 |
481 | Markdown will generate:
482 |
483 | This is a normal paragraph:
484 |
485 | This is a code block.
486 |
487 |
488 | One level of indentation -- 4 spaces or 1 tab -- is removed from each
489 | line of the code block. For example, this:
490 |
491 | Here is an example of AppleScript:
492 |
493 | tell application "Foo"
494 | beep
495 | end tell
496 |
497 | will turn into:
498 |
499 | Here is an example of AppleScript:
500 |
501 | tell application "Foo"
502 | beep
503 | end tell
504 |
505 |
506 | A code block continues until it reaches a line that is not indented
507 | (or the end of the article).
508 |
509 | Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
510 | are automatically converted into HTML entities. This makes it very
511 | easy to include example HTML source code using Markdown -- just paste
512 | it and indent it, and Markdown will handle the hassle of encoding the
513 | ampersands and angle brackets. For example, this:
514 |
515 |
518 |
519 | will turn into:
520 |
521 | <div class="footer">
522 | © 2004 Foo Corporation
523 | </div>
524 |
525 |
526 | Regular Markdown syntax is not processed within code blocks. E.g.,
527 | asterisks are just literal asterisks within a code block. This means
528 | it's also easy to use Markdown to write about Markdown's own syntax.
529 |
530 |
531 |
532 | Horizontal Rules
533 |
534 | You can produce a horizontal rule tag (` `) by placing three or
535 | more hyphens, asterisks, or underscores on a line by themselves. If you
536 | wish, you may use spaces between the hyphens or asterisks. Each of the
537 | following lines will produce a horizontal rule:
538 |
539 | * * *
540 |
541 | ***
542 |
543 | *****
544 |
545 | - - -
546 |
547 | ---------------------------------------
548 |
549 |
550 | * * *
551 |
552 | Span Elements
553 |
554 | Links
555 |
556 | Markdown supports two style of links: *inline* and *reference*.
557 |
558 | In both styles, the link text is delimited by [square brackets].
559 |
560 | To create an inline link, use a set of regular parentheses immediately
561 | after the link text's closing square bracket. Inside the parentheses,
562 | put the URL where you want the link to point, along with an *optional*
563 | title for the link, surrounded in quotes. For example:
564 |
565 | This is [an example](http://example.com/ "Title") inline link.
566 |
567 | [This link](http://example.net/) has no title attribute.
568 |
569 | Will produce:
570 |
571 | This is
572 | an example inline link.
573 |
574 | This link has no
575 | title attribute.
576 |
577 | If you're referring to a local resource on the same server, you can
578 | use relative paths:
579 |
580 | See my [About](/about/) page for details.
581 |
582 | Reference-style links use a second set of square brackets, inside
583 | which you place a label of your choosing to identify the link:
584 |
585 | This is [an example][id] reference-style link.
586 |
587 | You can optionally use a space to separate the sets of brackets:
588 |
589 | This is [an example] [id] reference-style link.
590 |
591 | Then, anywhere in the document, you define your link label like this,
592 | on a line by itself:
593 |
594 | [id]: http://example.com/ "Optional Title Here"
595 |
596 | That is:
597 |
598 | * Square brackets containing the link identifier (optionally
599 | indented from the left margin using up to three spaces);
600 | * followed by a colon;
601 | * followed by one or more spaces (or tabs);
602 | * followed by the URL for the link;
603 | * optionally followed by a title attribute for the link, enclosed
604 | in double or single quotes, or enclosed in parentheses.
605 |
606 | The following three link definitions are equivalent:
607 |
608 | [foo]: http://example.com/ "Optional Title Here"
609 | [foo]: http://example.com/ 'Optional Title Here'
610 | [foo]: http://example.com/ (Optional Title Here)
611 |
612 | **Note:** There is a known bug in Markdown.pl 1.0.1 which prevents
613 | single quotes from being used to delimit link titles.
614 |
615 | The link URL may, optionally, be surrounded by angle brackets:
616 |
617 | [id]: "Optional Title Here"
618 |
619 | You can put the title attribute on the next line and use extra spaces
620 | or tabs for padding, which tends to look better with longer URLs:
621 |
622 | [id]: http://example.com/longish/path/to/resource/here
623 | "Optional Title Here"
624 |
625 | Link definitions are only used for creating links during Markdown
626 | processing, and are stripped from your document in the HTML output.
627 |
628 | Link definition names may consist of letters, numbers, spaces, and
629 | punctuation -- but they are *not* case sensitive. E.g. these two
630 | links:
631 |
632 | [link text][a]
633 | [link text][A]
634 |
635 | are equivalent.
636 |
637 | The *implicit link name* shortcut allows you to omit the name of the
638 | link, in which case the link text itself is used as the name.
639 | Just use an empty set of square brackets -- e.g., to link the word
640 | "Google" to the google.com web site, you could simply write:
641 |
642 | [Google][]
643 |
644 | And then define the link:
645 |
646 | [Google]: http://google.com/
647 |
648 | Because link names may contain spaces, this shortcut even works for
649 | multiple words in the link text:
650 |
651 | Visit [Daring Fireball][] for more information.
652 |
653 | And then define the link:
654 |
655 | [Daring Fireball]: http://daringfireball.net/
656 |
657 | Link definitions can be placed anywhere in your Markdown document. I
658 | tend to put them immediately after each paragraph in which they're
659 | used, but if you want, you can put them all at the end of your
660 | document, sort of like footnotes.
661 |
662 | Here's an example of reference links in action:
663 |
664 | I get 10 times more traffic from [Google] [1] than from
665 | [Yahoo] [2] or [MSN] [3].
666 |
667 | [1]: http://google.com/ "Google"
668 | [2]: http://search.yahoo.com/ "Yahoo Search"
669 | [3]: http://search.msn.com/ "MSN Search"
670 |
671 | Using the implicit link name shortcut, you could instead write:
672 |
673 | I get 10 times more traffic from [Google][] than from
674 | [Yahoo][] or [MSN][].
675 |
676 | [google]: http://google.com/ "Google"
677 | [yahoo]: http://search.yahoo.com/ "Yahoo Search"
678 | [msn]: http://search.msn.com/ "MSN Search"
679 |
680 | Both of the above examples will produce the following HTML output:
681 |
682 | I get 10 times more traffic from Google than from
684 | Yahoo
685 | or MSN .
686 |
687 | For comparison, here is the same paragraph written using
688 | Markdown's inline link style:
689 |
690 | I get 10 times more traffic from [Google](http://google.com/ "Google")
691 | than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
692 | [MSN](http://search.msn.com/ "MSN Search").
693 |
694 | The point of reference-style links is not that they're easier to
695 | write. The point is that with reference-style links, your document
696 | source is vastly more readable. Compare the above examples: using
697 | reference-style links, the paragraph itself is only 81 characters
698 | long; with inline-style links, it's 176 characters; and as raw HTML,
699 | it's 234 characters. In the raw HTML, there's more markup than there
700 | is text.
701 |
702 | With Markdown's reference-style links, a source document much more
703 | closely resembles the final output, as rendered in a browser. By
704 | allowing you to move the markup-related metadata out of the paragraph,
705 | you can add links without interrupting the narrative flow of your
706 | prose.
707 |
708 |
709 | Emphasis
710 |
711 | Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
712 | emphasis. Text wrapped with one `*` or `_` will be wrapped with an
713 | HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML
714 | `` tag. E.g., this input:
715 |
716 | *single asterisks*
717 |
718 | _single underscores_
719 |
720 | **double asterisks**
721 |
722 | __double underscores__
723 |
724 | will produce:
725 |
726 | single asterisks
727 |
728 | single underscores
729 |
730 | double asterisks
731 |
732 | double underscores
733 |
734 | You can use whichever style you prefer; the lone restriction is that
735 | the same character must be used to open and close an emphasis span.
736 |
737 | Emphasis can be used in the middle of a word:
738 |
739 | un*frigging*believable
740 |
741 | But if you surround an `*` or `_` with spaces, it'll be treated as a
742 | literal asterisk or underscore.
743 |
744 | To produce a literal asterisk or underscore at a position where it
745 | would otherwise be used as an emphasis delimiter, you can backslash
746 | escape it:
747 |
748 | \*this text is surrounded by literal asterisks\*
749 |
750 |
751 |
752 | Code
753 |
754 | To indicate a span of code, wrap it with backtick quotes (`` ` ``).
755 | Unlike a pre-formatted code block, a code span indicates code within a
756 | normal paragraph. For example:
757 |
758 | Use the `printf()` function.
759 |
760 | will produce:
761 |
762 | Use the printf()
function.
763 |
764 | To include a literal backtick character within a code span, you can use
765 | multiple backticks as the opening and closing delimiters:
766 |
767 | ``There is a literal backtick (`) here.``
768 |
769 | which will produce this:
770 |
771 | There is a literal backtick (`) here.
772 |
773 | The backtick delimiters surrounding a code span may include spaces --
774 | one after the opening, one before the closing. This allows you to place
775 | literal backtick characters at the beginning or end of a code span:
776 |
777 | A single backtick in a code span: `` ` ``
778 |
779 | A backtick-delimited string in a code span: `` `foo` ``
780 |
781 | will produce:
782 |
783 | A single backtick in a code span: `
784 |
785 | A backtick-delimited string in a code span: `foo`
786 |
787 | With a code span, ampersands and angle brackets are encoded as HTML
788 | entities automatically, which makes it easy to include example HTML
789 | tags. Markdown will turn this:
790 |
791 | Please don't use any `` tags.
792 |
793 | into:
794 |
795 | Please don't use any <blink>
tags.
796 |
797 | You can write this:
798 |
799 | `—` is the decimal-encoded equivalent of `—`.
800 |
801 | to produce:
802 |
803 | —
is the decimal-encoded
804 | equivalent of —
.
805 |
806 |
807 |
808 | Images
809 |
810 | Admittedly, it's fairly difficult to devise a "natural" syntax for
811 | placing images into a plain text document format.
812 |
813 | Markdown uses an image syntax that is intended to resemble the syntax
814 | for links, allowing for two styles: *inline* and *reference*.
815 |
816 | Inline image syntax looks like this:
817 |
818 | 
819 |
820 | 
821 |
822 | That is:
823 |
824 | * An exclamation mark: `!`;
825 | * followed by a set of square brackets, containing the `alt`
826 | attribute text for the image;
827 | * followed by a set of parentheses, containing the URL or path to
828 | the image, and an optional `title` attribute enclosed in double
829 | or single quotes.
830 |
831 | Reference-style image syntax looks like this:
832 |
833 | ![Alt text][id]
834 |
835 | Where "id" is the name of a defined image reference. Image references
836 | are defined using syntax identical to link references:
837 |
838 | [id]: url/to/image "Optional title attribute"
839 |
840 | As of this writing, Markdown has no syntax for specifying the
841 | dimensions of an image; if this is important to you, you can simply
842 | use regular HTML ` ` tags.
843 |
844 |
845 | * * *
846 |
847 |
848 | Miscellaneous
849 |
850 | Automatic Links
851 |
852 | Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this:
853 |
854 |
855 |
856 | Markdown will turn this into:
857 |
858 | http://example.com/
859 |
860 | Automatic links for email addresses work similarly, except that
861 | Markdown will also perform a bit of randomized decimal and hex
862 | entity-encoding to help obscure your address from address-harvesting
863 | spambots. For example, Markdown will turn this:
864 |
865 |
866 |
867 | into something like this:
868 |
869 | address@exa
872 | mple.com
873 |
874 | which will render in a browser as a clickable link to "address@example.com".
875 |
876 | (This sort of entity-encoding trick will indeed fool many, if not
877 | most, address-harvesting bots, but it definitely won't fool all of
878 | them. It's better than nothing, but an address published in this way
879 | will probably eventually start receiving spam.)
880 |
881 |
882 |
883 | Backslash Escapes
884 |
885 | Markdown allows you to use backslash escapes to generate literal
886 | characters which would otherwise have special meaning in Markdown's
887 | formatting syntax. For example, if you wanted to surround a word
888 | with literal asterisks (instead of an HTML `` tag), you can use
889 | backslashes before the asterisks, like this:
890 |
891 | \*literal asterisks\*
892 |
893 | Markdown provides backslash escapes for the following characters:
894 |
895 | \ backslash
896 | ` backtick
897 | * asterisk
898 | _ underscore
899 | {} curly braces
900 | [] square brackets
901 | () parentheses
902 | # hash mark
903 | + plus sign
904 | - minus sign (hyphen)
905 | . dot
906 | ! exclamation mark
907 |
--------------------------------------------------------------------------------