sproxy 0.9.8 → 0.9.9
raw patch · 6 files changed
+91/−31 lines, 6 files
Files
- ChangeLog.md +8/−1
- README.md +21/−2
- sproxy.cabal +5/−1
- sproxy.sql +53/−23
- src/Authenticate/Google.hs +2/−1
- src/Authorize.hs +2/−3
ChangeLog.md view
@@ -1,7 +1,14 @@+0.9.9+=====++* This is the last release of this Sproxy. See [Sproxy2](http://hackage.haskell.org/package/sproxy2).+* Google: prompt for account only, don't ask for offline access.++ 0.9.8 ===== -* If the user is not authenticatedi, show login page with [HTTP status code+* If the user is not authenticated, show login page with [HTTP status code 511](https://tools.ietf.org/html/rfc6585), instead of 302 -> 200. It had bad UX for AJAX calls. * Always convert authenticated user's email to lowercase. This affects the
README.md view
@@ -9,9 +9,28 @@ * sproxy is independent. Any web application written in any language can use it. +## Use cases -## How it Works+ * Existing web applications with concept of roles. For example,+ [Mediawiki](https://www.mediawiki.org), [Jenkins](https://jenkins.io),+ [Icinga Web 2](https://www.icinga.org/products/icinga-web-2/). In+ this case you configure Sproxy to allow unrestricted access+ to the application for some groups defined by Sproxy. These+ groups are mapped to the application roles. There is a [plugin for+ Jenkins](https://wiki.jenkins-ci.org/display/JENKINS/Reverse+Proxy+Auth+Plugin)+ which can be used for this. Mediawiki and Icinga Web 2 were also+ successfully deployed in this way, though it required changes to their+ source code. + * New web applications designed to work specifically behind Sproxy. In this case+ you define Sproxy rules to control access to the+ application's API. It would likely be [a single-page+ application](https://en.wikipedia.org/wiki/Single-page_application).+ Examples are [MyWatch](https://hackage.haskell.org/package/mywatch) and+ [Juan de la Cosa](https://hackage.haskell.org/package/juandelacosa)++## How it works+ When an HTTP client makes a request, Sproxy checks for a *session cookie*. If it doesn't exist (or it's invalid, expired), it responses with [HTTP status 511](https://tools.ietf.org/html/rfc6585) with the page, where the@@ -119,7 +138,7 @@ devops | all | Access denied -## Configuration File+## Configuration file By default `sproxy` will read its configuration from `config/sproxy.yml`. There is example file with documentation
sproxy.cabal view
@@ -1,6 +1,10 @@ name: sproxy-version: 0.9.8+version: 0.9.9 synopsis: HTTP proxy for authenticating users via OAuth2+description:+ This version is no longer supported.+ Have a look at Sproxy2 (http://hackage.haskell.org/package/sproxy2),+ which goes far beyond Sproxy's features and limitations. license: MIT license-file: LICENSE copyright: 2013-2016, Zalora South East Asia Pte. Ltd
sproxy.sql view
@@ -1,9 +1,33 @@--- CREATE DATABASE sproxy;--- CREATE ROLE sproxy WITH LOGIN;--- GRANT SELECT ON ALL TABLES IN SCHEMA public TO sproxy;+/* +-- as super user:++-- NOT idempotent+CREATE DATABASE sproxy;+CREATE ROLE sproxy; -- this is for management tools like sproxy-web+CREATE ROLE "sproxy-readonly"; -- this is for sproxy itself (sic!)++-- idempotent from here on:+ALTER DATABASE sproxy OWNER TO sproxy;+ALTER ROLE "sproxy-readonly" LOGIN;+ALTER ROLE sproxy LOGIN;++\c sproxy;++SET ROLE sproxy;+-- as database owner (sproxy) from here on:++GRANT SELECT ON ALL TABLES IN SCHEMA public TO "sproxy-readonly";+ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "sproxy-readonly";++*/+++BEGIN;+ CREATE TABLE IF NOT EXISTS "group" (- "group" TEXT NOT NULL PRIMARY KEY+ "group" TEXT NOT NULL PRIMARY KEY,+ "comment" TEXT ); -- | group |@@ -13,11 +37,11 @@ -- | all | -- | regional | -CREATE EXTENSION IF NOT EXISTS citext; CREATE TABLE IF NOT EXISTS group_member ( "group" TEXT REFERENCES "group" ("group") ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,- email citext NOT NULL,+ email TEXT NOT NULL,+ "comment" TEXT, PRIMARY KEY ("group", email) ); @@ -33,59 +57,63 @@ -- SELECT "group" FROM group_member WHERE 'email.address' LIKE email CREATE TABLE IF NOT EXISTS domain (- domain TEXT NOT NULL PRIMARY KEY+ domain TEXT NOT NULL PRIMARY KEY,+ "comment" TEXT ); -- | domain | -- |-----------------------|--- | app1.example.com |--- | app2.example.com |--- | app3.example.com |+-- | app1.example.com |+-- | app2.example.com |+-- | app3.example.com | CREATE TABLE IF NOT EXISTS privilege ( "domain" TEXT REFERENCES domain (domain) ON UPDATE CASCADE ON DELETE CASCADE NOT NULL, privilege TEXT NOT NULL,+ "comment" TEXT, PRIMARY KEY ("domain", privilege) ); -- | domain | privilege | -- |-----------------------+------------|--- | app3.example.com | view |--- | app3.example.com | export |--- | app1.example.com | list users |--- | app1.example.com | add users |+-- | app3.example.com | view |+-- | app3.example.com | export |+-- | app1.example.com | list users |+-- | app1.example.com | add users | CREATE TABLE IF NOT EXISTS privilege_rule ( "domain" TEXT NOT NULL, privilege TEXT NOT NULL, "path" TEXT NOT NULL, "method" TEXT NOT NULL,+ "comment" TEXT, FOREIGN KEY ("domain", privilege) REFERENCES privilege ("domain", privilege) ON UPDATE CASCADE ON DELETE CASCADE, PRIMARY KEY ("domain", "path", "method") ); -- | domain | privilege | path | method | -- |-----------------------+------------+-----------+--------|--- | app3.example.com | view | /% | % |--- | app3.example.com | export | /export/% | % |--- | app1.example.com | list users | /users | GET |--- | app1.example.com | list users | /user/% | GET |--- | app1.example.com | add users | /users | POST |+-- | app3.example.com | view | /% | % |+-- | app3.example.com | export | /export/% | % |+-- | app1.example.com | list users | /users | GET |+-- | app1.example.com | list users | /user/% | GET |+-- | app1.example.com | add users | /users | POST | CREATE TABLE IF NOT EXISTS group_privilege ( "group" TEXT REFERENCES "group" ("group") ON UPDATE CASCADE ON DELETE CASCADE NOT NULL, "domain" TEXT NOT NULL, privilege TEXT NOT NULL,+ "comment" TEXT, FOREIGN KEY ("domain", privilege) REFERENCES privilege ("domain", privilege) ON UPDATE CASCADE ON DELETE CASCADE, PRIMARY KEY ("group", "domain", privilege) ); -- | group | domain | privilege | -- |--------------+-----------------------+------------|--- | data science | app3.example.com | view |--- | data science | app3.example.com | export |--- | all | app1.example.com | list users |--- | devops | app1.example.com | add users |+-- | data science | app3.example.com | view |+-- | data science | app3.example.com | export |+-- | all | app1.example.com | list users |+-- | devops | app1.example.com | add users | -- Check if the user is authorized for the request. Let's break it -- down for understanding:@@ -135,4 +163,6 @@ INSERT INTO group_privilege ("group", domain, privilege) VALUES ('dev', 'example.com', 'full'); INSERT INTO privilege_rule (domain, privilege, path, method) VALUES ('example.com', 'full', '%', '%'); */++END;
src/Authenticate/Google.hs view
@@ -33,7 +33,8 @@ , "&state=", urlEncode True path , "&redirect_uri=", redirectUri base , "&response_type=code&client_id=", cid- , "&approval_prompt=force&access_type=offline"+ , "&prompt=select_account"+ , "&access_type=offline" ] authenticate :: AuthConfig -> ByteString -> ByteString -> ByteString -> IO (Response BodyReader)
src/Authorize.hs view
@@ -59,9 +59,8 @@ WHERE ? LIKE email AND ? LIKE "domain" AND privilege IN (- SELECT p.privilege FROM privilege p- INNER JOIN privilege_rule pr ON pr."domain" = p."domain" AND pr.privilege = p.privilege- WHERE ? LIKE pr."domain" AND ? LIKE "path" AND ? ILIKE "method"+ SELECT privilege FROM privilege_rule+ WHERE ? LIKE "domain" AND ? LIKE "path" AND ? ILIKE "method" ORDER by array_length(regexp_split_to_array("path", '/'), 1) DESC LIMIT 1 ) |] (email, domain, domain, path, method)