├── docs
├── pagebreak.tex
├── images
│ ├── OpenID.png
│ ├── AdaSecurity.jpg
│ ├── PolicyModel.png
│ └── ModelOverview.png
├── Security_Auth_OAuth_Googleplus.md
├── requirements.txt
├── title.md
├── Security_Controllers.md
├── Security_Random.md
├── Security_OAuth_JWT.md
├── index.md
├── links.txt
├── Installation.md
├── Security_OAuth_Servers.md
├── Security_Auth.md
├── pandoc.css
├── Security.md
├── Security_OAuth.md
└── Security_Policies.md
├── uml
└── security.zargo
├── regtests
├── files
│ ├── permissions
│ │ ├── empty.xml
│ │ ├── simple-policy.xml
│ │ ├── policy-with-role.xml
│ │ └── grants.xml
│ ├── discover
│ │ ├── steamcommunity.xrds
│ │ ├── livejournal.xrds
│ │ ├── myspace.xrds
│ │ ├── orange.xrds
│ │ ├── google.xrds
│ │ ├── claimid.xrds
│ │ ├── yahoo.xrds
│ │ ├── verisign.xrds
│ │ └── myopenid.xrds
│ └── user_apps.properties
├── src
│ ├── security-testsuite.ads
│ ├── security_harness.adb
│ ├── security-random-tests.ads
│ ├── security-oauth-clients-tests.ads
│ ├── security-oauth-jwt-tests.ads
│ ├── security-auth-tests.ads
│ ├── security-testsuite.adb
│ ├── security-oauth-servers-tests.ads
│ ├── security-permissions-tests.ads
│ ├── security-policies-tests.ads
│ ├── security-random-tests.adb
│ ├── security-permissions-tests.adb
│ ├── security-oauth-jwt-tests.adb
│ ├── security-oauth-clients-tests.adb
│ └── security-auth-tests.adb
├── security_tests.gpr
└── alire.toml
├── samples
├── web
│ ├── atlas
│ │ ├── images
│ │ │ ├── icons-1.png
│ │ │ └── open-id-logos-1.png
│ │ ├── login.html
│ │ └── css
│ │ │ ├── grids
│ │ │ ├── reset.css
│ │ │ ├── reset_rtl.css
│ │ │ └── fluid_grid.css
│ │ │ ├── users.css
│ │ │ └── awa.css
│ └── success.thtml
├── samples.gpr
├── alire.toml
└── src
│ ├── auth_cb.ads
│ └── auth_demo.adb
├── .readthedocs.yaml
├── mkdocs.yml
├── dynamo.xml
├── coverage.sh
├── NOTICE.txt
├── security.gpr
├── src
├── security-auth-oauth-yahoo.ads
├── security-auth-oauth-github.ads
├── security-controllers-urls.adb
├── security-controllers-urls.ads
├── security-auth-oauth-googleplus.ads
├── security-controllers-roles.ads
├── security-auth-oauth-facebook.ads
├── security-controllers-roles.adb
├── security-controllers.ads
├── security-auth-oauth-github.adb
├── security-oauth.adb
├── security-random.ads
├── security-auth-oauth.ads
├── security-permissions.ads
├── security-auth-oauth-googleplus.adb
├── security-oauth-jwt.ads
├── security-auth-oauth-yahoo.adb
├── security-auth-openid.ads
├── security.ads
├── security-oauth.ads
├── security-auth-oauth.adb
├── security-random.adb
└── security-oauth-jwt.adb
├── Makefile
├── NEWS.md
├── security_config.gpr
├── alire.toml
├── samples.properties
└── README.md
/docs/pagebreak.tex:
--------------------------------------------------------------------------------
1 | \newpage
2 |
--------------------------------------------------------------------------------
/uml/security.zargo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stcarrez/ada-security/HEAD/uml/security.zargo
--------------------------------------------------------------------------------
/docs/images/OpenID.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stcarrez/ada-security/HEAD/docs/images/OpenID.png
--------------------------------------------------------------------------------
/regtests/files/permissions/empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/docs/images/AdaSecurity.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stcarrez/ada-security/HEAD/docs/images/AdaSecurity.jpg
--------------------------------------------------------------------------------
/docs/images/PolicyModel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stcarrez/ada-security/HEAD/docs/images/PolicyModel.png
--------------------------------------------------------------------------------
/docs/images/ModelOverview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stcarrez/ada-security/HEAD/docs/images/ModelOverview.png
--------------------------------------------------------------------------------
/samples/web/atlas/images/icons-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stcarrez/ada-security/HEAD/samples/web/atlas/images/icons-1.png
--------------------------------------------------------------------------------
/samples/web/atlas/images/open-id-logos-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stcarrez/ada-security/HEAD/samples/web/atlas/images/open-id-logos-1.png
--------------------------------------------------------------------------------
/docs/Security_Auth_OAuth_Googleplus.md:
--------------------------------------------------------------------------------
1 | ### Google+
2 | The Google+ authentication is based on OAuth 2.0 and the OpenID Connect Basic Client Profile.
3 |
4 | See https://developers.google.com/accounts/docs/OAuth2Login
5 |
6 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | build:
4 | os: ubuntu-22.04
5 | tools:
6 | python: "3.11"
7 |
8 | mkdocs:
9 | configuration: mkdocs.yml
10 |
11 | python:
12 | install:
13 | - requirements: docs/requirements.txt
14 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | mkdocs==1.6.1
2 | mkdocs-material==9.5.34
3 | Pygments==2.18.0
4 | pymdown-extensions==10.9
5 | markdown==3.7
6 | mkdocs-markdownextradata-plugin==0.2.6
7 | markdown-include==0.8.1
8 | mkdocs-redirects==1.2.1
9 | jinja2==3.1.4
10 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: Ada Security Library
2 | docs_dir: docs
3 | nav:
4 | - Introduction: index.md
5 | - Installation: Installation.md
6 | - Overview: Security.md
7 | - Authentication: Security_Auth.md
8 | - OAuth2: Security_OAuth.md
9 | - Policies: Security_Policies.md
10 | theme: readthedocs
11 |
--------------------------------------------------------------------------------
/dynamo.xml:
--------------------------------------------------------------------------------
1 |
2 | security
3 | docs/links.txt
4 | Stephane.Carrez@gmail.com
5 | .;../ada-util
6 | Stephane Carrez
7 |
8 |
--------------------------------------------------------------------------------
/regtests/files/discover/steamcommunity.xrds:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | http://specs.openid.net/auth/2.0/server
6 | https://steamcommunity.com/openid/login
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/title.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Ada Security Library Programmer's Guide"
3 | author: [Stephane Carrez]
4 | date: 2024-10-12
5 | subject: "Ada Security Library"
6 | tags: [Ada, Security, Permission, OAuth2, Policies, JWT]
7 | titlepage: true
8 | titlepage-color: 06386e
9 | titlepage-text-color: FFFFFF
10 | titlepage-rule-color: FFFFFF
11 | titlepage-rule-height: 1
12 | ...
13 |
--------------------------------------------------------------------------------
/regtests/files/discover/livejournal.xrds:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | http://specs.openid.net/auth/2.0/signon
5 | http://www.livejournal.com/openid/server.bml
6 | http://joe.livejournal.com/
7 |
8 |
9 |
--------------------------------------------------------------------------------
/coverage.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | NAME=security.cov
3 | alr exec -- lcov --quiet --base-directory . --directory . \
4 | --no-external --ignore-errors gcov,unused \
5 | --exclude '*/' \
6 | --exclude '*/b__*.adb' \
7 | --exclude '*/samples/*' \
8 | --exclude '*/regtests*' -c -o $NAME
9 | rm -rf cover
10 | genhtml --quiet --ignore-errors source -o ./cover -t "test coverage" --num-spaces 4 $NAME
11 |
12 |
--------------------------------------------------------------------------------
/regtests/files/user_apps.properties:
--------------------------------------------------------------------------------
1 | apps.list=1,2,tst3
2 | apps.1.client_id=app-id-1
3 | apps.1.client_secret=app-secret-1
4 | apps.2.client_id=app-id-2
5 | apps.2.client_secret=app-secret-2
6 | apps.tst3.client_id=app-id-tst3
7 | apps.tst3.client_secret=app-secret-tst3
8 |
9 | users.list=joe,admin
10 | users.joe.username=joe
11 | users.joe.password=test
12 | users.admin.username=admin
13 | users.admin.password=admin
14 |
--------------------------------------------------------------------------------
/NOTICE.txt:
--------------------------------------------------------------------------------
1 | Ada Security Library
2 | Copyright 2011-2021 Stephane Carrez
3 |
4 | This product includes software developed by:
5 | Stephane Carrez (https://blog.vacs.fr/).
6 |
7 | ------------------------------------------------------------------------
8 | See the file LICENSE.txt
9 | See licenses for accompanying products in the "/licenses" subdirectory.
10 | ------------------------------------------------------------------------
11 |
12 |
--------------------------------------------------------------------------------
/regtests/files/discover/myspace.xrds:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | http://specs.openid.net/auth/2.0/signon
9 | http://specs.openid.net/extensions/oauth/1.0
10 | http://api.myspace.com/openid
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/Security_Controllers.md:
--------------------------------------------------------------------------------
1 | ## Security Controller
2 | The Security.Controllers package defines the security controller used to
3 | verify that a given permission is granted. A security controller uses the security
4 | context and other controller specific and internal data to verify that the permission
5 | is granted.
6 |
7 | Security controller instances are created when the security policy rules are parsed.
8 | These instances are shared across possibly several concurrent requests.
9 |
10 |
--------------------------------------------------------------------------------
/regtests/files/discover/orange.xrds:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | http://specs.openid.net/auth/2.0/server
6 | http://openid.net/srv/ax/1.0
7 | http://openid.net/sreg/1.0
8 | http://openid.net/extensions/sreg/1.1
9 | http://openid.orange.fr/server/
10 |
11 |
12 |
--------------------------------------------------------------------------------
/regtests/src/security-testsuite.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- Security testsuite - Ada Security Test suite
3 | -- Copyright (C) 2011, 2012 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Tests;
9 |
10 | package Security.Testsuite is
11 |
12 | function Suite return Util.Tests.Access_Test_Suite;
13 |
14 | end Security.Testsuite;
15 |
--------------------------------------------------------------------------------
/docs/Security_Random.md:
--------------------------------------------------------------------------------
1 | ## Random Generator
2 | The Security.Random package defines the Generator tagged type
3 | which provides operations to generate random tokens intended to be used for
4 | a nonce, access token, salt or other purposes. The generator is intended to be
5 | used in multi-task environments as it implements the low level random generation
6 | within a protected type. The generator defines a Generate operation
7 | that returns either a binary random array or the base64url encoding of the
8 | binary array.
9 |
10 |
--------------------------------------------------------------------------------
/regtests/files/discover/google.xrds:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | http://specs.openid.net/auth/2.0/server
6 | http://openid.net/srv/ax/1.0
7 | http://specs.openid.net/extensions/ui/1.0/mode/popup
8 | http://specs.openid.net/extensions/ui/1.0/icon
9 | http://specs.openid.net/extensions/pape/1.0
10 | https://www.google.com/accounts/o8/ud
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/regtests/src/security_harness.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- Security -- Unit tests for the Ada Security
3 | -- Copyright (C) 2012 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Tests;
9 | with Security.Testsuite;
10 | procedure Security_Harness is
11 |
12 | procedure Harness is new Util.Tests.Harness (Security.Testsuite.Suite);
13 |
14 | begin
15 | Harness ("security-tests.xml");
16 | end Security_Harness;
17 |
--------------------------------------------------------------------------------
/samples/samples.gpr:
--------------------------------------------------------------------------------
1 | with "security_config";
2 | with "utilada_sys";
3 | with "utilada_aws";
4 | with "security";
5 | with "aws";
6 |
7 | project Samples is
8 |
9 | Mains := ("auth_demo.adb");
10 |
11 | for Main use Mains;
12 |
13 | for Source_Dirs use ("src");
14 |
15 | for Object_Dir use "../" & Security_Config'Object_Dir & "/obj";
16 | for Exec_Dir use "../bin";
17 |
18 | package Binder renames Security_Config.Binder;
19 | package Builder renames Security_Config.Builder;
20 | package Compiler renames Security_Config.Compiler;
21 | package Linker renames Security_Config.Linker;
22 |
23 | end Samples;
24 |
--------------------------------------------------------------------------------
/regtests/security_tests.gpr:
--------------------------------------------------------------------------------
1 | with "security_config";
2 | with "utilada_unit";
3 | with "security";
4 |
5 | project Security_Tests is
6 |
7 | for Languages use ("Ada");
8 |
9 | Mains := ("security_harness.adb");
10 |
11 | for Source_Dirs use ("src");
12 |
13 | for Main use Mains;
14 |
15 | for Object_Dir use "../" & Security_Config'Object_Dir & "/obj/regtests";
16 | for Exec_Dir use "../bin";
17 |
18 | package Binder renames Security_Config.Binder;
19 | package Builder renames Security_Config.Builder;
20 | package Compiler renames Security_Config.Compiler;
21 | package Linker renames Security_Config.Linker;
22 |
23 | end Security_Tests;
24 |
--------------------------------------------------------------------------------
/regtests/alire.toml:
--------------------------------------------------------------------------------
1 | description = "Uni tests for Ada Security Library"
2 | name = "security_tests"
3 | version = "1.5.1"
4 | licenses = "Apache-2.0"
5 | maintainers = ["Stephane.Carrez@gmail.com"]
6 | maintainers-logins = ["stcarrez"]
7 | authors = ["Stephane.Carrez@gmail.com"]
8 | project-files = ["security_tests.gpr"]
9 | tags = ["security", "oauth2", "authentication", "permissions", "jwt"]
10 | website = "https://gitlab.com/stcarrez/ada-security"
11 | long-description = """
12 |
13 | Unit tests for Ada Security
14 |
15 | """
16 |
17 | [[depends-on]]
18 | security = "*"
19 | utilada = "*"
20 | utilada_unit = "*"
21 |
22 | [configuration]
23 | disabled = true
24 |
25 | [[pins]]
26 | security = { path = '..' }
27 |
--------------------------------------------------------------------------------
/regtests/files/discover/claimid.xrds:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | http://openid.net/signon/1.0
9 | https://openid.claimid.com/server
10 | https://openid.claimid.com/joe
11 |
12 |
13 | http://openid.net/signon/1.0
14 | https://openid.claimid.com/server
15 | https://openid.claimid.com/joe
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/samples/alire.toml:
--------------------------------------------------------------------------------
1 | description = "Examples for Ada Security Library"
2 | name = "security_samples"
3 | version = "1.5.2"
4 | licenses = "Apache-2.0"
5 | maintainers = ["Stephane.Carrez@gmail.com"]
6 | maintainers-logins = ["stcarrez"]
7 | authors = ["Stephane.Carrez@gmail.com"]
8 | project-files = ["samples.gpr"]
9 | tags = ["security", "oauth2", "authentication", "permissions", "jwt"]
10 | website = "https://gitlab.com/stcarrez/ada-security"
11 | long-description = """
12 |
13 | Examples for Ada Security
14 |
15 | """
16 |
17 | [[depends-on]]
18 | security = "*"
19 | utilada = "*"
20 | utilada_aws = "*"
21 | aws = "^24.0"
22 |
23 | [configuration]
24 | disabled = true
25 |
26 | [[pins]]
27 | security = { path = '..' }
28 |
--------------------------------------------------------------------------------
/regtests/src/security-random-tests.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-random-tests - Tests for random package
3 | -- Copyright (C) 2017, 2022 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Tests;
9 | package Security.Random.Tests is
10 |
11 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite);
12 |
13 | type Test is new Util.Tests.Test with null record;
14 |
15 | procedure Test_Generate (T : in out Test);
16 |
17 | procedure Test_Generate_String (T : in out Test);
18 |
19 | end Security.Random.Tests;
20 |
--------------------------------------------------------------------------------
/docs/Security_OAuth_JWT.md:
--------------------------------------------------------------------------------
1 | ### JSON Web Token
2 | JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred
3 | between two parties. A JWT token is returned by an authorization server. It contains
4 | useful information that allows to verify the authentication and identify the user.
5 |
6 | The Security.OAuth.JWT package implements the decoding part of JWT defined in:
7 | JSON Web Token (JWT), http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-07
8 |
9 | A list of pre-defined ID tokens are returned in the JWT token claims and used for
10 | the OpenID Connect. This is specified in
11 | OpenID Connect Basic Client Profile 1.0 - draft 26,
12 | http://openid.net/specs/openid-connect-basic-1_0.html
13 |
14 |
15 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | Ada Security provides a security framework which allows applications to define
4 | and enforce security policies. This framework allows users to authenticate by using
5 | [OpenID Authentication 2.0](http://openid.net/specs/openid-authentication-2_0.html)
6 | as well as [OAuth 2.0](http://oauth.net/2/) protocol.
7 | It allows a web application to integrate easily with Yahoo!, Facebook and
8 | Google+ authentication systems.
9 | The Ada05 library includes:
10 |
11 | * An OpenID client authentication,
12 | * An OAuth 2.0 client authentication,
13 | * An OpenID Connect authentication framework,
14 | * An OAuth 2.0 server authentication framework,
15 | * A policy based security framework to protect the resources
16 |
17 | 
18 |
19 |
20 |
--------------------------------------------------------------------------------
/security.gpr:
--------------------------------------------------------------------------------
1 | with "security_config";
2 | with "utilada_sys";
3 | with "utilada_xml";
4 |
5 | library project Security is
6 |
7 | Version := "1.5.1";
8 | Library_Dir := "lib";
9 | Library_Type : Security_Config.Library_Type_Type := external ("SECURITY_LIBRARY_TYPE", "static");
10 |
11 | for Source_Dirs use ("src");
12 | for Library_Name use "security";
13 | for Library_Kind use Library_Type;
14 | for Library_Version use "lib" & Project'Library_Name & ".so." & Version;
15 |
16 | for Library_Dir use Library_Dir & "/security/" & Project'Library_Kind;
17 | for Object_Dir use "./" & Security_Config'Object_Dir & "/obj/security/" & Project'Library_Kind;
18 |
19 | package Binder renames Security_Config.Binder;
20 | package Builder renames Security_Config.Builder;
21 | package Compiler renames Security_Config.Compiler;
22 | package Ide renames Security_Config.Ide;
23 |
24 | end Security;
25 |
--------------------------------------------------------------------------------
/regtests/files/permissions/simple-policy.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | developer
4 |
5 |
6 | manager
7 |
8 |
9 | admin
10 |
11 |
12 | admin
13 | admin
14 | manager
15 |
16 |
17 | anonymous
18 |
19 |
20 | logged-user
21 |
22 |
23 | admin
24 | /admin/.*
25 |
26 |
27 | logged-user
28 | /user/.*
29 |
30 |
31 | anonymous
32 | /.*
33 |
34 |
35 |
--------------------------------------------------------------------------------
/docs/links.txt:
--------------------------------------------------------------------------------
1 | Ada Server Faces https://github.com/stcarrez/ada-asf
2 | Ada Database Objects https://github.com/stcarrez/ada-ado
3 | Ada Utility Library https://github.com/stcarrez/ada-util
4 | Ada Wiki https://github.com/stcarrez/ada-wiki
5 | Ada EL https://github.com/stcarrez/ada-el
6 | Ada Security https://github.com/stcarrez/ada-security
7 | AWS https://github.com/AdaCore/aws
8 | Dynamo https://github.com/stcarrez/dynamo
9 | Java Bean https://en.wikipedia.org/wiki/JavaBean
10 | Java Log4j https://logging.apache.org/log4j/2.x/
11 | Log4cxx https://logging.apache.org/log4cxx/latest_stable/index.html
12 | RFC7231 https://tools.ietf.org/html/rfc7231
13 | RFC 6749 https://tools.ietf.org/html/rfc6749
14 | OpenID Authentication 2.0 https://openid.net/specs/openid-authentication-2_0.html
15 | OpenID Connect Core 1.0 https://openid.net/specs/openid-connect-core-1_0.html
16 |
--------------------------------------------------------------------------------
/regtests/src/security-oauth-clients-tests.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- Security-oauth-clients-tests - Unit tests for OAuth
3 | -- Copyright (C) 2013 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Tests;
9 |
10 | package Security.OAuth.Clients.Tests is
11 |
12 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite);
13 |
14 | type Test is new Util.Tests.Test with null record;
15 |
16 | -- Test Create_Nonce operation.
17 | procedure Test_Create_Nonce (T : in out Test);
18 |
19 | -- Test the Get_State operation.
20 | procedure Test_Get_State (T : in out Test);
21 |
22 | -- Test the Is_Valid_State operation.
23 | procedure Test_Is_Valid_State (T : in out Test);
24 |
25 | -- Test the Get_Auth_Params operation.
26 | procedure Test_Get_Auth_Params (T : in out Test);
27 |
28 | end Security.OAuth.Clients.Tests;
29 |
--------------------------------------------------------------------------------
/regtests/src/security-oauth-jwt-tests.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- Security-oayth-jwt-tests - Unit tests for JSON Web Token
3 | -- Copyright (C) 2013 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Tests;
9 |
10 | package Security.OAuth.JWT.Tests is
11 |
12 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite);
13 |
14 | type Test is new Util.Tests.Test with null record;
15 |
16 | -- Test Decode operation with errors.
17 | procedure Test_Decode_Error (T : in out Test);
18 |
19 | generic
20 | with function Get (From : in Token) return String;
21 | Value : String;
22 | procedure Test_Operation (T : in out Test);
23 |
24 | generic
25 | with function Get (From : in Token) return Ada.Calendar.Time;
26 | Value : String;
27 | procedure Test_Time_Operation (T : in out Test);
28 |
29 | end Security.OAuth.JWT.Tests;
30 |
--------------------------------------------------------------------------------
/regtests/files/discover/yahoo.xrds:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | http://specs.openid.net/auth/2.0/server
9 | http://specs.openid.net/extensions/pape/1.0
10 | http://openid.net/srv/ax/1.0
11 | http://specs.openid.net/extensions/oauth/1.0
12 | http://specs.openid.net/extensions/ui/1.0/lang-pref
13 | http://specs.openid.net/extensions/ui/1.0/mode/popup
14 | http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier
15 | http://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf
16 | http://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdf
17 | http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf
18 | https://open.login.yahooapis.com/openid/op/auth
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/regtests/files/permissions/policy-with-role.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | developer
4 |
5 |
6 | manager
7 |
8 |
9 | admin
10 |
11 |
12 |
13 | developer
14 | developer
15 | manager
16 |
17 |
18 |
19 | manager
20 | admin
21 | manager
22 |
23 |
24 |
25 | admin
26 | admin
27 | manager
28 |
29 |
30 |
31 | admin
32 | /admin/.*
33 |
34 |
35 |
36 | developer
37 | /developer/.*
38 |
39 |
40 |
41 | manager
42 | /manager/.*
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/security-auth-oauth-yahoo.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-auth-oauth-yahoo -- Yahoo! OAuth based authentication
3 | -- Copyright (C) 2013, 2014, 2020 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Security.OAuth.Clients;
9 | package Security.Auth.OAuth.Yahoo is
10 |
11 | -- ------------------------------
12 | -- OAuth Manager
13 | -- ------------------------------
14 | -- The Manager provides the core operations for the OAuth authorization process.
15 | type Manager is new Security.Auth.OAuth.Manager with null record;
16 |
17 | -- Verify the OAuth access token and retrieve information about the user.
18 | overriding
19 | procedure Verify_Access_Token (Realm : in Manager;
20 | Assoc : in Association;
21 | Request : in Parameters'Class;
22 | Token : in Security.OAuth.Clients.Access_Token_Access;
23 | Result : in out Authentication);
24 |
25 | end Security.Auth.OAuth.Yahoo;
26 |
--------------------------------------------------------------------------------
/regtests/files/discover/verisign.xrds:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 | http://specs.openid.net/auth/2.0/signon
10 | http://openid.net/sreg/1.0
11 | http://openid.net/extensions/sreg/1.1
12 | http://schemas.openid.net/pape/policies/2007/06/phishing-resistant
13 | http://schemas.openid.net/pape/policies/2007/06/multi-factor
14 | http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical
15 | http://pip.verisignlabs.com/server
16 | http://joe.pip.verisignlabs.com/
17 |
18 |
19 |
20 | http://openid.net/signon/1.1
21 | http://openid.net/sreg/1.0
22 | http://openid.net/extensions/sreg/1.1
23 | http://pip.verisignlabs.com/server
24 | http://joe.pip.verisignlabs.com/
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/security-auth-oauth-github.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-auth-oauth-github -- Github OAuth based authentication
3 | -- Copyright (C) 2013, 2014, 2020 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Security.OAuth.Clients;
9 | package Security.Auth.OAuth.Github is
10 |
11 | -- ------------------------------
12 | -- OAuth Manager
13 | -- ------------------------------
14 | -- The Manager provides the core operations for the OAuth authorization process.
15 | type Manager is new Security.Auth.OAuth.Manager with null record;
16 |
17 | -- Verify the OAuth access token and retrieve information about the user.
18 | overriding
19 | procedure Verify_Access_Token (Realm : in Manager;
20 | Assoc : in Association;
21 | Request : in Parameters'Class;
22 | Token : in Security.OAuth.Clients.Access_Token_Access;
23 | Result : in out Authentication);
24 |
25 | end Security.Auth.OAuth.Github;
26 |
--------------------------------------------------------------------------------
/regtests/src/security-auth-tests.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-openid - Tests for OpenID
3 | -- Copyright (C) 2009, 2010, 2011, 2012, 2013 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Strings.Maps;
9 | with Util.Tests;
10 | package Security.Auth.Tests is
11 |
12 | use Ada.Strings.Unbounded;
13 |
14 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite);
15 |
16 | type Test is new Util.Tests.Test with null record;
17 |
18 | procedure Test_Discovery (T : in out Test);
19 |
20 | procedure Test_Verify_Signature (T : in out Test);
21 |
22 | type Test_Parameters is new Security.Auth.Parameters with record
23 | Params : Util.Strings.Maps.Map;
24 | end record;
25 |
26 | overriding
27 | function Get_Parameter (Params : in Test_Parameters;
28 | Name : in String) return String;
29 |
30 | procedure Set_Parameter (Params : in out Test_Parameters;
31 | Name : in String;
32 | Value : in String);
33 |
34 | end Security.Auth.Tests;
35 |
--------------------------------------------------------------------------------
/regtests/src/security-testsuite.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- Security testsuite - Ada Security Test suite
3 | -- Copyright (C) 2011, 2012, 2013, 2017 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 | with Security.Auth.Tests;
8 | with Security.Permissions.Tests;
9 | with Security.Policies.Tests;
10 | with Security.OAuth.JWT.Tests;
11 | with Security.OAuth.Clients.Tests;
12 | with Security.OAuth.Servers.Tests;
13 | with Security.Random.Tests;
14 | package body Security.Testsuite is
15 |
16 | Tests : aliased Util.Tests.Test_Suite;
17 |
18 | function Suite return Util.Tests.Access_Test_Suite is
19 | Ret : constant Util.Tests.Access_Test_Suite := Tests'Access;
20 | begin
21 | Security.Random.Tests.Add_Tests (Ret);
22 | Security.OAuth.JWT.Tests.Add_Tests (Ret);
23 | Security.Auth.Tests.Add_Tests (Ret);
24 | Security.Permissions.Tests.Add_Tests (Ret);
25 | Security.Policies.Tests.Add_Tests (Ret);
26 | Security.OAuth.Clients.Tests.Add_Tests (Ret);
27 | Security.OAuth.Servers.Tests.Add_Tests (Ret);
28 | return Ret;
29 | end Suite;
30 |
31 | end Security.Testsuite;
32 |
--------------------------------------------------------------------------------
/regtests/files/permissions/grants.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | developer
4 |
5 |
6 | manager
7 |
8 |
9 | admin
10 |
11 |
12 |
13 | create-ticket
14 | developer
15 | manager
16 |
17 |
18 |
19 | update-ticket
20 | developer
21 | manager
22 |
23 |
24 |
25 | delete-ticket
26 | developer
27 | manager
28 |
29 |
30 |
31 | add-user
32 | admin
33 | manager
34 |
35 |
36 |
37 | remove-user
38 | admin
39 | manager
40 |
41 |
42 |
43 | admin
44 | /admin/.*
45 |
46 |
47 |
48 | create-ticket
49 | /developer/.*
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/src/security-controllers-urls.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-controllers-urls -- URL permission controller
3 | -- Copyright (C) 2012 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | package body Security.Controllers.URLs is
9 |
10 | -- ------------------------------
11 | -- Returns true if the user associated with the security context Context has
12 | -- the permission to access the URL defined in Permission .
13 | -- ------------------------------
14 | overriding
15 | function Has_Permission (Handler : in URL_Controller;
16 | Context : in Security.Contexts.Security_Context'Class;
17 | Permission : in Security.Permissions.Permission'Class)
18 | return Boolean is
19 | begin
20 | if Permission in Security.Policies.URLs.URL_Permission'Class then
21 | return Handler.Manager.Has_Permission (Context,
22 | Policies.URLs.URL_Permission'Class (Permission));
23 | else
24 | return False;
25 | end if;
26 | end Has_Permission;
27 |
28 | end Security.Controllers.URLs;
29 |
--------------------------------------------------------------------------------
/regtests/src/security-oauth-servers-tests.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- Security-oauth-servers-tests - Unit tests for server side OAuth
3 | -- Copyright (C) 2017, 2018 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Tests;
9 |
10 | package Security.OAuth.Servers.Tests is
11 |
12 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite);
13 |
14 | type Test is new Util.Tests.Test with null record;
15 |
16 | -- Test the application manager.
17 | procedure Test_Application_Manager (T : in out Test);
18 |
19 | -- Test the user registration and verification.
20 | procedure Test_User_Verify (T : in out Test);
21 |
22 | -- Test the token operation that produces an access token from user/password.
23 | -- RFC 6749: Section 4.3. Resource Owner Password Credentials Grant
24 | procedure Test_Token_Password (T : in out Test);
25 |
26 | -- Test the access token validation with invalid tokens (bad formed).
27 | procedure Test_Bad_Token (T : in out Test);
28 |
29 | -- Test the loading configuration files for the File_Registry.
30 | procedure Test_Load_Registry (T : in out Test);
31 |
32 | end Security.OAuth.Servers.Tests;
33 |
--------------------------------------------------------------------------------
/regtests/src/security-permissions-tests.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- Security-permissions-tests - Unit tests for Security.Permissions
3 | -- Copyright (C) 2011, 2012, 2016 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Tests;
9 |
10 | package Security.Permissions.Tests is
11 |
12 | package P_Admin is new Permissions.Definition ("admin");
13 | package P_Create is new Permissions.Definition ("create");
14 | package P_Update is new Permissions.Definition ("update");
15 | package P_Delete is new Permissions.Definition ("delete");
16 |
17 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite);
18 |
19 | type Test is new Util.Tests.Test with null record;
20 |
21 | -- Test Add_Permission and Get_Permission_Index.
22 | procedure Test_Add_Permission (T : in out Test);
23 |
24 | -- Test the permission created by the Definition package.
25 | procedure Test_Define_Permission (T : in out Test);
26 |
27 | -- Test Get_Permission on invalid permission name.
28 | procedure Test_Get_Invalid_Permission (T : in out Test);
29 |
30 | -- Test operations on the Permission_Index_Set.
31 | procedure Test_Add_Permission_Set (T : in out Test);
32 |
33 | end Security.Permissions.Tests;
34 |
--------------------------------------------------------------------------------
/src/security-controllers-urls.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-controllers-urls -- URL permission controller
3 | -- Copyright (C) 2012 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Security.Contexts;
9 | with Security.Permissions;
10 |
11 | with Security.Policies.URLs;
12 |
13 | package Security.Controllers.URLs is
14 |
15 | -- ------------------------------
16 | -- Security Controller
17 | -- ------------------------------
18 | -- The URL_Controller implements the permission check for URL permissions.
19 | -- It uses the URL policy manager to verify the permission.
20 | type URL_Controller is limited new Controller with record
21 | Manager : Security.Policies.URLs.URL_Policy_Access;
22 | end record;
23 | type URL_Controller_Access is access all URL_Controller'Class;
24 |
25 | -- Returns true if the user associated with the security context Context has
26 | -- the permission to access the URL defined in Permission .
27 | overriding
28 | function Has_Permission (Handler : in URL_Controller;
29 | Context : in Security.Contexts.Security_Context'Class;
30 | Permission : in Security.Permissions.Permission'Class)
31 | return Boolean;
32 |
33 | end Security.Controllers.URLs;
34 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | NAME=security
2 | VERSION=1.5.2
3 |
4 | DIST_DIR=ada-security-$(VERSION)
5 | DIST_FILE=ada-security-$(VERSION).tar.gz
6 |
7 | MAKE_ARGS += -XSECURITY_BUILD=$(BUILD)
8 |
9 | -include Makefile.conf
10 |
11 | STATIC_MAKE_ARGS = $(MAKE_ARGS) -XSECURITY_LIBRARY_TYPE=static
12 | SHARED_MAKE_ARGS = $(MAKE_ARGS) -XSECURITY_LIBRARY_TYPE=relocatable
13 | SHARED_MAKE_ARGS += -XUTILADA_BASE_BUILD=relocatable -XUTIL_LIBRARY_TYPE=relocatable
14 | SHARED_MAKE_ARGS += -XXMLADA_BUILD=relocatable
15 | SHARED_MAKE_ARGS += -XLIBRARY_TYPE=relocatable
16 |
17 | include Makefile.defaults
18 |
19 | build-test:: lib-setup
20 | cd regtests && $(BUILD_COMMAND) $(GPRFLAGS) $(MAKE_ARGS)
21 |
22 | # Build and run the unit tests
23 | test: build
24 | bin/security_harness -l $(NAME): -xml security-aunit.xml
25 |
26 | samples:
27 | cd samples && $(BUILD_COMMAND) $(GPRFLAGS) $(MAKE_ARGS)
28 |
29 | SECURITY_DOC= \
30 | title.md \
31 | pagebreak.tex \
32 | index.md \
33 | pagebreak.tex \
34 | Installation.md \
35 | pagebreak.tex \
36 | Security.md \
37 | pagebreak.tex \
38 | Security_Auth.md \
39 | pagebreak.tex \
40 | Security_OAuth.md \
41 | pagebreak.tex \
42 | Security_Policies.md
43 |
44 | DOC_OPTIONS=-f markdown --listings --number-sections --toc
45 | HTML_OPTIONS=-f markdown --listings --number-sections --toc --css pandoc.css
46 |
47 | $(eval $(call ada_library,$(NAME),.))
48 | $(eval $(call pandoc_build,security-book,$(SECURITY_DOC)))
49 | $(eval $(call alire_publish,alire.toml,se/security,security-$(VERSION).toml))
50 |
51 | .PHONY: samples
52 |
--------------------------------------------------------------------------------
/samples/web/success.thtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Success
9 |
10 |
11 |
12 |
13 |
14 |
Successfully authentified as
15 |
ID:@_ID_@
16 |
EMAIL:@_EMAIL_@
17 |
18 |
Login again
19 |
20 |
21 |
22 |
23 |
24 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/security-auth-oauth-googleplus.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-auth-oauth-googleplus -- Google+ OAuth based authentication
3 | -- Copyright (C) 2013 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Security.OAuth.Clients;
9 |
10 | -- === Google+ ===
11 | -- The Google+ authentication is based on OAuth 2.0 and the OpenID Connect Basic Client Profile.
12 | --
13 | -- See https://developers.google.com/accounts/docs/OAuth2Login
14 | package Security.Auth.OAuth.Googleplus is
15 |
16 | -- ------------------------------
17 | -- OAuth Manager
18 | -- ------------------------------
19 | -- The Manager provides the core operations for the OAuth authorization process.
20 | type Manager is new Security.Auth.OAuth.Manager with private;
21 |
22 | -- Verify the OAuth access token and retrieve information about the user.
23 | overriding
24 | procedure Verify_Access_Token (Realm : in Manager;
25 | Assoc : in Association;
26 | Request : in Parameters'Class;
27 | Token : in Security.OAuth.Clients.Access_Token_Access;
28 | Result : in out Authentication);
29 |
30 | private
31 |
32 | type Manager is new Security.Auth.OAuth.Manager with null record;
33 |
34 | end Security.Auth.OAuth.Googleplus;
35 |
--------------------------------------------------------------------------------
/src/security-controllers-roles.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-controllers-roles -- Simple role base security
3 | -- Copyright (C) 2011, 2012 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Security.Contexts;
9 | with Security.Permissions;
10 |
11 | with Security.Policies.Roles;
12 |
13 | package Security.Controllers.Roles is
14 |
15 | -- ------------------------------
16 | -- Security Controller
17 | -- ------------------------------
18 | -- The Role_Controller implements a simple role based permissions check.
19 | -- The permission is granted if the user has the role defined by the controller.
20 | type Role_Controller (Count : Positive) is limited new Controller with record
21 | Roles : Policies.Roles.Role_Type_Array (1 .. Count);
22 | end record;
23 | type Role_Controller_Access is access all Role_Controller'Class;
24 |
25 | -- Returns true if the user associated with the security context Context has
26 | -- one of the role defined in the Handler .
27 | overriding
28 | function Has_Permission (Handler : in Role_Controller;
29 | Context : in Security.Contexts.Security_Context'Class;
30 | Permission : in Security.Permissions.Permission'Class)
31 | return Boolean;
32 |
33 | end Security.Controllers.Roles;
34 |
--------------------------------------------------------------------------------
/regtests/files/discover/myopenid.xrds:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | http://specs.openid.net/auth/2.0/signon
9 | http://openid.net/sreg/1.0
10 | http://openid.net/extensions/sreg/1.1
11 | http://schemas.openid.net/pape/policies/2007/06/phishing-resistant
12 | http://openid.net/srv/ax/1.0
13 | http://www.myopenid.com/server
14 | http://joe.myopenid.com/
15 |
16 |
17 | http://openid.net/signon/1.1
18 | http://openid.net/sreg/1.0
19 | http://openid.net/extensions/sreg/1.1
20 | http://schemas.openid.net/pape/policies/2007/06/phishing-resistant
21 | http://openid.net/srv/ax/1.0
22 | http://www.myopenid.com/server
23 | http://joe.myopenid.com/
24 |
25 |
26 | http://openid.net/signon/1.0
27 | http://openid.net/sreg/1.0
28 | http://openid.net/extensions/sreg/1.1
29 | http://schemas.openid.net/pape/policies/2007/06/phishing-resistant
30 | http://openid.net/srv/ax/1.0
31 | http://www.myopenid.com/server
32 | http://joe.myopenid.com/
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/security-auth-oauth-facebook.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-auth-oauth-facebook -- Facebook OAuth based authentication
3 | -- Copyright (C) 2013, 2014 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Security.OAuth.Clients;
9 | package Security.Auth.OAuth.Facebook is
10 |
11 | -- ------------------------------
12 | -- OAuth Manager
13 | -- ------------------------------
14 | -- The Manager provides the core operations for the OAuth authorization process.
15 | type Manager is new Security.Auth.OAuth.Manager with private;
16 |
17 | -- Initialize the authentication realm.
18 | overriding
19 | procedure Initialize (Realm : in out Manager;
20 | Params : in Parameters'Class;
21 | Provider : in String := PROVIDER_OPENID);
22 |
23 | -- Verify the OAuth access token and retrieve information about the user.
24 | overriding
25 | procedure Verify_Access_Token (Realm : in Manager;
26 | Assoc : in Association;
27 | Request : in Parameters'Class;
28 | Token : in Security.OAuth.Clients.Access_Token_Access;
29 | Result : in out Authentication);
30 |
31 | private
32 |
33 | type Manager is new Security.Auth.OAuth.Manager with record
34 | App_Access_Token : Ada.Strings.Unbounded.Unbounded_String;
35 | end record;
36 |
37 | end Security.Auth.OAuth.Facebook;
38 |
--------------------------------------------------------------------------------
/NEWS.md:
--------------------------------------------------------------------------------
1 | Version 1.5.2 - Under development
2 | - Fix #11: Ada.Calendar.Conversions.To_Struct_Timespec is deprecated
3 |
4 | Version 1.5.1 - Aug 2024
5 | - Cleanup build environment to drop configure
6 |
7 | Version 1.5.0 - Jul 2023
8 | - Add Create function for API key grant types
9 |
10 | Version 1.4.1 - Aug 2022
11 | - Fix Alire GNAT project to build in debug mode
12 | - Fix Security.Random that generates shorter random string
13 |
14 | Version 1.4.0 - Dec 2020
15 | - Add support to authenticate with Gitlab and GitHub
16 | - Update Yahoo! authenticate to use OpenId connect
17 | - Update the AWS demo to add Gitlab and Github
18 |
19 | Version 1.3.1 - Nov 2020
20 | - Fix AWS example
21 |
22 | Version 1.3.0 - May 2020
23 | - Add support to extend the authenticate manager and allow to
24 | use custom authentication through the Set_Default_Factory operation.
25 |
26 | Version 1.2.1 - Dec 2019
27 | - Cleanup the build process
28 |
29 | Version 1.2.0 - Jul 2018
30 | - OAuth 2.0 server implementation (RFC 6749)
31 | - Improvement of the role based security policy
32 |
33 | Version 1.1.2 - Dec 2015
34 | - Improvement of configure and installation process with gprinstall (if available)
35 |
36 | Version 1.1.1 - Jul 2014
37 | - Fix minor configuration issue with GNAT 2014
38 |
39 | Version 1.1 - Mar 2014
40 | - New authentication framework that supports OpenID, OpenID Connect, OAuth, Facebook login
41 | - AWS demo for a Google, Yahoo!, Facebook, Google+ authentication
42 | - Support to extract JSON Web Token (JWT)
43 | - Support for the creation of Debian packages
44 |
45 | Version 1.0 - Jan 2013
46 | - OpenID implementation based on Ada Server Faces
47 | - OAuth 2.0 client implementation
48 | - Security policy core implementation
49 |
--------------------------------------------------------------------------------
/docs/Installation.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | This chapter explains how to build and install the library.
4 |
5 | ## Using Alire
6 |
7 | To use Ada Security in your project, run the following command to add the dependency
8 | for your project:
9 |
10 | ```
11 | alr index --update-all
12 | alr with security
13 | ```
14 |
15 | ## Without Alire
16 |
17 | If you don't have [Alire](https://alire.ada.dev/) or want to build and install the library
18 | on a specific place, run a `setup` command to configure the build as well as installation
19 | directory.
20 |
21 | The `HAVE_ALIRE` configuration allows you to disable the build with [Alire](https://alire.ada.dev/):
22 |
23 | ```
24 | make setup BUILD=debug PREFIX=/build/install HAVE_ALIRE=no
25 | ```
26 |
27 | Since this build method does not verify that all dependencies are met, make sure that you
28 | have already built and install the following components and they are available to `gprbuild`
29 | through `ADA_PROJECT_PATH` if needed:
30 |
31 | * [Ada Utility Library](https://gitlab.com/stcarrez/ada-util/)
32 |
33 | Then build, run the unit tests and install by using:
34 |
35 | ```
36 | make
37 | make test
38 | make install
39 | ```
40 |
41 | To use the installed libraries, make sure your `ADA_PROJECT_PATH` contains the directory
42 | where you installed the libraries (configured by the `PREFIX=` option in the setup phase).
43 | The installed GNAT projects are the same as those used when using [Alire](https://alire.ada.dev/).
44 |
45 | If you want to install on a specific place, you can change the `prefix` and indicate the installation
46 | direction as follows:
47 |
48 | ```
49 | make install prefix=/opt
50 | ```
51 |
52 | ## Using
53 |
54 | To use the library in an Ada project, add the following line at the beginning of your GNAT project file:
55 |
56 | ```
57 | with "security";
58 | ```
59 |
--------------------------------------------------------------------------------
/src/security-controllers-roles.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-controllers-roles -- Simple role base security
3 | -- Copyright (C) 2011, 2012, 2018 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | package body Security.Controllers.Roles is
9 |
10 | -- ------------------------------
11 | -- Returns true if the user associated with the security context Context has
12 | -- one of the role defined in the Handler .
13 | -- ------------------------------
14 | overriding
15 | function Has_Permission (Handler : in Role_Controller;
16 | Context : in Security.Contexts.Security_Context'Class;
17 | Permission : in Security.Permissions.Permission'Class)
18 | return Boolean is
19 | pragma Unreferenced (Permission);
20 |
21 | P : constant Security.Principal_Access := Context.Get_User_Principal;
22 | Roles : Security.Policies.Roles.Role_Map;
23 | begin
24 | if P /= null then
25 | -- If the principal has some roles, get them.
26 | if P.all in Policies.Roles.Role_Principal_Context'Class then
27 | Roles := Policies.Roles.Role_Principal_Context'Class (P.all).Get_Roles;
28 | else
29 | return False;
30 | end if;
31 |
32 | for I in Handler.Roles'Range loop
33 | if Roles (Handler.Roles (I)) then
34 | return True;
35 | end if;
36 | end loop;
37 | end if;
38 | return False;
39 | end Has_Permission;
40 |
41 | end Security.Controllers.Roles;
42 |
--------------------------------------------------------------------------------
/src/security-controllers.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-controllers -- Controllers to verify a security permission
3 | -- Copyright (C) 2011, 2012 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Security.Contexts;
9 | with Security.Permissions;
10 |
11 | -- == Security Controller ==
12 | -- The Security.Controllers package defines the security controller used to
13 | -- verify that a given permission is granted. A security controller uses the security
14 | -- context and other controller specific and internal data to verify that the permission
15 | -- is granted.
16 | --
17 | -- Security controller instances are created when the security policy rules are parsed.
18 | -- These instances are shared across possibly several concurrent requests.
19 | package Security.Controllers is
20 |
21 | Invalid_Controller : exception;
22 |
23 | -- ------------------------------
24 | -- Security Controller interface
25 | -- ------------------------------
26 | type Controller is limited interface;
27 | type Controller_Access is access all Controller'Class;
28 |
29 | -- Checks whether the permission defined by the Handler controller data is granted
30 | -- by the security context passed in Context .
31 | -- Returns true if such permission is granted.
32 | function Has_Permission (Handler : in Controller;
33 | Context : in Security.Contexts.Security_Context'Class;
34 | Permission : in Security.Permissions.Permission'Class)
35 | return Boolean is abstract;
36 |
37 | end Security.Controllers;
38 |
--------------------------------------------------------------------------------
/samples/src/auth_cb.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- auth_cb -- Authentication callback examples
3 | -- Copyright (C) 2013 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with AWS.Response;
9 | with AWS.Status;
10 | with Util.Properties;
11 | with Security.Auth;
12 | package Auth_CB is
13 |
14 | -- Implement the first step of authentication: discover the OpenID (if any) provider,
15 | -- create the authorization request and redirect the user to the authorization server.
16 | -- Some authorization data is saved in the session for the verify process.
17 | -- The authorization provider is defined according to the base URI which is configured
18 | -- by the Auth_Config properties. The following names are recognized:
19 | --
20 | -- google yahoo orange facebook
21 | --
22 | -- Each authorization provider has its own set of configuration parameter which defines
23 | -- what implementation to use (OpenID or OAuth) and how to configure and connect
24 | -- to the server. The google, yahoo and orange map to the openid implementation.
25 | -- The facebook maps to the facebook implementation (based on OAuth).
26 |
27 | function Get_Authorization (Request : in AWS.Status.Data) return AWS.Response.Data;
28 |
29 | -- Second step of authentication: verify the authorization response. The authorization
30 | -- data saved in the session is extracted and checked against the response. If it matches
31 | -- the response is verified to check if the authentication succeeded or not.
32 | -- The user is then redirected to the success page.
33 | function Verify_Authorization (Request : in AWS.Status.Data) return AWS.Response.Data;
34 |
35 | -- Display information about the current user.
36 | function User_Info (Request : in AWS.Status.Data) return AWS.Response.Data;
37 |
38 | type Auth_Config is new Util.Properties.Manager and Security.Auth.Parameters with null record;
39 |
40 | overriding
41 | function Get_Parameter (Params : in Auth_Config;
42 | Name : in String) return String;
43 |
44 | Config : Auth_Config;
45 |
46 | end Auth_CB;
47 |
--------------------------------------------------------------------------------
/security_config.gpr:
--------------------------------------------------------------------------------
1 | abstract project Security_Config is
2 | for Source_Dirs use ();
3 |
4 | type Yes_No is ("yes", "no");
5 |
6 | type Library_Type_Type is ("relocatable", "static", "static-pic");
7 |
8 | type Build_Type is ("distrib", "debug", "optimize", "profile", "coverage");
9 | Mode : Build_Type := external ("SECURITY_BUILD", "debug");
10 |
11 | Processors := External ("PROCESSORS", "0");
12 |
13 | package Builder is
14 | for Default_Switches ("Ada") use ("-j" & Processors);
15 | end Builder;
16 |
17 | package compiler is
18 | warnings := ("-gnatwua");
19 | defaults := ("-gnat2012");
20 | case Mode is
21 | when "distrib" =>
22 | for Default_Switches ("Ada") use defaults & ("-O2", "-gnatafno", "-gnatVa", "-gnatwa");
23 |
24 | when "debug" =>
25 | for Default_Switches ("Ada") use defaults & warnings
26 | & ("-g", "-gnata", "-gnatVaMI", "-gnaty3abcefhiklmnprstxM99");
27 |
28 | when "coverage" =>
29 | for Default_Switches ("Ada") use defaults & warnings
30 | & ("-g", "-O2", "-gnata", "-gnatVaMI", "-gnaty3abcefhiklmnprstxM99",
31 | "-fprofile-arcs", "-ftest-coverage");
32 |
33 | when "optimize" =>
34 | for Default_Switches ("Ada") use defaults & warnings
35 | & ("-O2", "-gnatn", "-gnatp", "-fdata-sections", "-ffunction-sections");
36 |
37 | when "profile" =>
38 | for Default_Switches ("Ada") use defaults & warnings & ("-pg");
39 |
40 | end case;
41 | end compiler;
42 |
43 | package binder is
44 | case Mode is
45 | when "debug" =>
46 | for Default_Switches ("Ada") use ("-E");
47 |
48 | when others =>
49 | for Default_Switches ("Ada") use ("-E");
50 |
51 | end case;
52 | end binder;
53 |
54 | package linker is
55 | case Mode is
56 | when "profile" =>
57 | for Default_Switches ("Ada") use ("-pg");
58 |
59 | when "distrib" =>
60 | for Default_Switches ("Ada") use ("-s");
61 |
62 | when "optimize" =>
63 | for Default_Switches ("Ada") use ("-Wl,--gc-sections");
64 |
65 | when "coverage" =>
66 | for Default_Switches ("ada") use ("-fprofile-arcs");
67 |
68 | when others =>
69 | null;
70 | end case;
71 |
72 | end linker;
73 |
74 | package Ide is
75 | for VCS_Kind use "git";
76 | end Ide;
77 |
78 | end Security_Config;
79 |
--------------------------------------------------------------------------------
/src/security-auth-oauth-github.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-auth-oauth-github -- Github OAuth based authentication
3 | -- Copyright (C) 2020, 2021 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Log.Loggers;
9 | with Util.Http.Clients;
10 | with Util.Properties.JSON;
11 | package body Security.Auth.OAuth.Github is
12 |
13 | use Util.Log;
14 |
15 | Log : constant Loggers.Logger := Util.Log.Loggers.Create ("Security.Auth.OAuth.Github");
16 |
17 | -- ------------------------------
18 | -- Verify the OAuth access token and retrieve information about the user.
19 | -- ------------------------------
20 | overriding
21 | procedure Verify_Access_Token (Realm : in Manager;
22 | Assoc : in Association;
23 | Request : in Parameters'Class;
24 | Token : in Security.OAuth.Clients.Access_Token_Access;
25 | Result : in out Authentication) is
26 | pragma Unreferenced (Assoc, Request);
27 |
28 | URI : constant String := "https://api.github.com/user";
29 | Http : Util.Http.Clients.Client;
30 | Reply : Util.Http.Clients.Response;
31 | Props : Util.Properties.Manager;
32 | begin
33 | Http.Add_Header ("Authorization", "token " & Token.Get_Name);
34 | Http.Get (URI, Reply);
35 | if Reply.Get_Status /= Util.Http.SC_OK then
36 | Log.Warn ("Cannot retrieve Github user information");
37 | Set_Result (Result, INVALID_SIGNATURE, "invalid access token");
38 | return;
39 | end if;
40 |
41 | Util.Properties.JSON.Parse_JSON (Props, Reply.Get_Body);
42 |
43 | Result.Identity := Realm.Issuer;
44 | Append (Result.Identity, "/");
45 | Append (Result.Identity, String '(Props.Get ("id")));
46 | Result.Claimed_Id := Result.Identity;
47 | Result.First_Name := To_Unbounded_String (Props.Get ("name"));
48 | Result.Full_Name := To_Unbounded_String (Props.Get ("name"));
49 |
50 | -- The email is optional and depends on the scope.
51 | Result.Email := To_Unbounded_String (Props.Get ("email"));
52 | Set_Result (Result, AUTHENTICATED, "authenticated");
53 | end Verify_Access_Token;
54 |
55 | end Security.Auth.OAuth.Github;
56 |
--------------------------------------------------------------------------------
/alire.toml:
--------------------------------------------------------------------------------
1 | description = "Security Library for HTTP client and server with OAuth2 support"
2 | name = "security"
3 | version = "1.5.2"
4 | licenses = "Apache-2.0"
5 | maintainers = ["Stephane.Carrez@gmail.com"]
6 | maintainers-logins = ["stcarrez"]
7 | authors = ["Stephane.Carrez@gmail.com"]
8 | project-files = ["security.gpr"]
9 | tags = ["security", "oauth2", "authentication", "permissions", "jwt"]
10 | website = "https://gitlab.com/stcarrez/ada-security"
11 | long-description = """
12 |
13 | [](https://porion.vacs.fr/porion/projects/view/ada-security/summary)
14 | [](https://porion.vacs.fr/porion/projects/view/ada-securit/xunits)
15 | [](https://porion.vacs.fr/porion/projects/view/ada-security/summary)
16 | [](https://ada-security.readthedocs.io/en/latest/?badge=latest)
17 |
18 | Ada Security provides a security framework which allows applications to define
19 | and enforce security policies. This framework allows users to authenticate by using
20 | [OpenID Authentication 2.0](https://openid.net/specs/openid-authentication-2_0.html)
21 | as well as [OAuth 2.0](https://oauth.net/2/) protocol.
22 | It allows a web application to integrate easily with Yahoo!, Gitlab, Github, Facebook and
23 | Google+ authentication systems.
24 | The Ada05 library includes:
25 |
26 | * An OpenID client authentication,
27 | * An OAuth 2.0 client authentication,
28 | * An OpenID Connect authentication framework,
29 | * An OAuth 2.0 server authentication framework,
30 | * A policy based security framework to protect the resources
31 |
32 | The Ada Security library is used by the
33 | [Ada Web Application](https://gitlab.com/stcarrez/ada-awa)
34 | to provide authentication and access control to users within the web applications.
35 |
36 | ## Documentation
37 |
38 | * [Ada Security Programmer's Guide](https://ada-security.readthedocs.io/en/latest/)
39 |
40 | """
41 |
42 | [[depends-on]]
43 | utilada = "^2.8.0"
44 | utilada_xml = "^2.8.0"
45 |
46 | [gpr-externals]
47 | SECURITY_BUILD = ["distrib", "debug", "optimize", "profile", "coverage"]
48 | SECURITY_LIBRARY_TYPE = ["relocatable", "static", "static-pic"]
49 |
50 | [configuration]
51 | disabled = true
52 |
--------------------------------------------------------------------------------
/src/security-oauth.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-oauth -- OAuth Security
3 | -- Copyright (C) 2017, 2018 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 | package body Security.OAuth is
8 |
9 | use Ada.Strings.Unbounded;
10 |
11 | -- ------------------------------
12 | -- Get the application identifier.
13 | -- ------------------------------
14 | function Get_Application_Identifier (App : in Application) return String is
15 | begin
16 | return To_String (App.Client_Id);
17 | end Get_Application_Identifier;
18 |
19 | -- ------------------------------
20 | -- Set the application identifier used by the OAuth authorization server
21 | -- to identify the application (for example, the App ID in Facebook).
22 | -- ------------------------------
23 | procedure Set_Application_Identifier (App : in out Application;
24 | Client : in String) is
25 | begin
26 | App.Client_Id := To_Unbounded_String (Client);
27 | end Set_Application_Identifier;
28 |
29 | -- ------------------------------
30 | -- Set the application secret defined in the OAuth authorization server
31 | -- for the application (for example, the App Secret in Facebook).
32 | -- ------------------------------
33 | procedure Set_Application_Secret (App : in out Application;
34 | Secret : in String) is
35 | begin
36 | App.Secret := To_Unbounded_String (Secret);
37 | end Set_Application_Secret;
38 |
39 | -- ------------------------------
40 | -- Set the redirection callback that will be used to redirect the user
41 | -- back to the application after the OAuth authorization is finished.
42 | -- ------------------------------
43 | procedure Set_Application_Callback (App : in out Application;
44 | URI : in String) is
45 | begin
46 | App.Callback := To_Unbounded_String (URI);
47 | end Set_Application_Callback;
48 |
49 | -- ------------------------------
50 | -- Set the client authentication method used when doing OAuth calls for this application.
51 | -- See RFC 6749, 2.3. Client Authentication
52 | -- ------------------------------
53 | procedure Set_Client_Authentication (App : in out Application;
54 | Method : in Client_Authentication_Type) is
55 | begin
56 | App.Client_Auth := Method;
57 | end Set_Client_Authentication;
58 |
59 | end Security.OAuth;
60 |
--------------------------------------------------------------------------------
/samples/web/atlas/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Log in
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
Connect with...
18 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/regtests/src/security-policies-tests.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- Security-policies-tests - Unit tests for Security.Permissions
3 | -- Copyright (C) 2011, 2012, 2013, 2017, 2022 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Tests;
9 | with Util.Strings;
10 |
11 | with Security.Policies.Roles;
12 |
13 | package Security.Policies.Tests is
14 |
15 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite);
16 |
17 | type Test is new Util.Tests.Test with null record;
18 |
19 | -- Test Create_Role and Get_Role_Name
20 | procedure Test_Create_Role (T : in out Test);
21 |
22 | -- Test Set_Roles
23 | procedure Test_Set_Roles (T : in out Test);
24 |
25 | -- Test Set_Roles on an invalid role name
26 | procedure Test_Set_Invalid_Roles (T : in out Test);
27 |
28 | -- Test the Get_Policy, Get_Role_Policy and Add_Policy operations.
29 | procedure Test_Get_Role_Policy (T : in out Test);
30 |
31 | -- Test Has_Permission
32 | procedure Test_Has_Permission (T : in out Test);
33 |
34 | -- Test reading an empty policy file
35 | procedure Test_Read_Empty_Policy (T : in out Test);
36 |
37 | -- Test reading policy files
38 | procedure Test_Read_Policy (T : in out Test);
39 |
40 | -- Test reading policy files and using the controller
41 | procedure Test_Role_Policy (T : in out Test);
42 |
43 | -- Test the Get_Grants operation.
44 | procedure Test_Grants (T : in out Test);
45 |
46 | -- Test anonymous users and permissions.
47 | procedure Test_Anonymous_Permission (T : in out Test);
48 |
49 | -- Read the policy file File and perform a test on the given URI
50 | -- with a user having the given role.
51 | procedure Check_Policy (T : in out Test;
52 | File : in String;
53 | Role : in String;
54 | URL : in String;
55 | Anonymous : in Boolean := False;
56 | Granted : in Boolean := True);
57 |
58 | type Test_Principal is new Principal and Roles.Role_Principal_Context with record
59 | Name : Util.Strings.String_Ref;
60 | Roles : Security.Policies.Roles.Role_Map := (others => False);
61 | end record;
62 |
63 | -- Get the roles assigned to the user.
64 | overriding
65 | function Get_Roles (User : in Test_Principal) return Roles.Role_Map;
66 |
67 | -- Get the principal name.
68 | overriding
69 | function Get_Name (From : in Test_Principal) return String;
70 |
71 | end Security.Policies.Tests;
72 |
--------------------------------------------------------------------------------
/src/security-random.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-random -- Random numbers for nonce, secret keys, token generation
3 | -- Copyright (C) 2017, 2023 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 | with Ada.Streams;
8 | with Ada.Finalization;
9 | with Ada.Strings.Unbounded;
10 | private with Ada.Numerics.Discrete_Random;
11 | private with Interfaces;
12 |
13 | -- == Random Generator ==
14 | -- The Security.Random package defines the Generator tagged type
15 | -- which provides operations to generate random tokens intended to be used for
16 | -- a nonce, access token, salt or other purposes. The generator is intended to be
17 | -- used in multi-task environments as it implements the low level random generation
18 | -- within a protected type. The generator defines a Generate operation
19 | -- that returns either a binary random array or the base64url encoding of the
20 | -- binary array.
21 | package Security.Random is
22 |
23 | type Generator is limited new Ada.Finalization.Limited_Controlled with private;
24 |
25 | -- Initialize the random generator.
26 | overriding
27 | procedure Initialize (Gen : in out Generator);
28 |
29 | -- Fill the array with pseudo-random numbers.
30 | procedure Generate (Gen : in out Generator;
31 | Into : out Ada.Streams.Stream_Element_Array);
32 | procedure Generate (Gen : in out Generator;
33 | Into : out String);
34 |
35 | -- Generate a random sequence of bits and convert the result
36 | -- into a string in base64url.
37 | function Generate (Gen : in out Generator'Class;
38 | Bits : in Positive) return String;
39 |
40 | -- Generate a random sequence of bits, convert the result
41 | -- into a string in base64url and append it to the buffer.
42 | procedure Generate (Gen : in out Generator'Class;
43 | Bits : in Positive;
44 | Into : in out Ada.Strings.Unbounded.Unbounded_String);
45 |
46 | private
47 |
48 | package Id_Random is new Ada.Numerics.Discrete_Random (Interfaces.Unsigned_32);
49 |
50 | -- Protected type to allow using the random generator by several tasks.
51 | protected type Raw_Generator is
52 |
53 | procedure Generate (Into : out Ada.Streams.Stream_Element_Array);
54 |
55 | procedure Reset;
56 | private
57 | -- Random number generator used for ID generation.
58 | Rand : Id_Random.Generator;
59 | end Raw_Generator;
60 |
61 | type Generator is limited new Ada.Finalization.Limited_Controlled with record
62 | Rand : Raw_Generator;
63 | end record;
64 |
65 | end Security.Random;
66 |
--------------------------------------------------------------------------------
/samples/web/atlas/css/grids/reset.css:
--------------------------------------------------------------------------------
1 | /* `XHTML, HTML4, HTML5 Reset
2 | ----------------------------------------------------------------------------------------------------*/
3 |
4 | a,
5 | abbr,
6 | acronym,
7 | address,
8 | applet,
9 | article,
10 | aside,
11 | audio,
12 | b,
13 | big,
14 | blockquote,
15 | body,
16 | canvas,
17 | caption,
18 | center,
19 | cite,
20 | code,
21 | dd,
22 | del,
23 | details,
24 | dfn,
25 | dialog,
26 | div,
27 | dl,
28 | dt,
29 | em,
30 | embed,
31 | fieldset,
32 | figcaption,
33 | figure,
34 | font,
35 | footer,
36 | form,
37 | h1,
38 | h2,
39 | h3,
40 | h4,
41 | h5,
42 | h6,
43 | header,
44 | hgroup,
45 | hr,
46 | html,
47 | i,
48 | iframe,
49 | img,
50 | ins,
51 | kbd,
52 | label,
53 | legend,
54 | li,
55 | mark,
56 | menu,
57 | meter,
58 | nav,
59 | object,
60 | ol,
61 | output,
62 | p,
63 | pre,
64 | progress,
65 | q,
66 | rp,
67 | rt,
68 | ruby,
69 | s,
70 | samp,
71 | section,
72 | small,
73 | span,
74 | strike,
75 | strong,
76 | sub,
77 | summary,
78 | sup,
79 | table,
80 | tbody,
81 | td,
82 | tfoot,
83 | th,
84 | thead,
85 | time,
86 | tr,
87 | tt,
88 | u,
89 | ul,
90 | var,
91 | video,
92 | xmp {
93 | border: 0;
94 | margin: 0;
95 | padding: 0;
96 | font-size: 100%;
97 | }
98 |
99 | html,
100 | body {
101 | height: 100%;
102 | }
103 |
104 | article,
105 | aside,
106 | details,
107 | figcaption,
108 | figure,
109 | footer,
110 | header,
111 | hgroup,
112 | menu,
113 | nav,
114 | section {
115 | /*
116 | Override the default (display: inline) for
117 | browsers that do not recognize HTML5 tags.
118 |
119 | IE8 (and lower) requires a shiv:
120 | http://ejohn.org/blog/html5-shiv
121 | */
122 | display: block;
123 | }
124 |
125 | b,
126 | strong {
127 | /*
128 | Makes browsers agree.
129 | IE + Opera = font-weight: bold.
130 | Gecko + WebKit = font-weight: bolder.
131 | */
132 | font-weight: bold;
133 | }
134 |
135 | img {
136 | color: transparent;
137 | font-size: 0;
138 | vertical-align: middle;
139 | /*
140 | For IE.
141 | http://css-tricks.com/ie-fix-bicubic-scaling-for-images
142 | */
143 | -ms-interpolation-mode: bicubic;
144 | }
145 |
146 | li {
147 | /*
148 | For IE6 + IE7.
149 | */
150 | display: list-item;
151 | }
152 |
153 | table {
154 | border-collapse: collapse;
155 | border-spacing: 0;
156 | }
157 |
158 | th,
159 | td,
160 | caption {
161 | font-weight: normal;
162 | vertical-align: top;
163 | text-align: left;
164 | }
165 |
166 | q {
167 | quotes: none;
168 | }
169 |
170 | q:before,
171 | q:after {
172 | content: '';
173 | content: none;
174 | }
175 |
176 | sub,
177 | sup,
178 | small {
179 | font-size: 75%;
180 | }
181 |
182 | sub,
183 | sup {
184 | line-height: 0;
185 | position: relative;
186 | vertical-align: baseline;
187 | }
188 |
189 | sub {
190 | bottom: -0.25em;
191 | }
192 |
193 | sup {
194 | top: -0.5em;
195 | }
196 |
197 | svg {
198 | /*
199 | For IE9.
200 | */
201 | overflow: hidden;
202 | }
--------------------------------------------------------------------------------
/samples/web/atlas/css/grids/reset_rtl.css:
--------------------------------------------------------------------------------
1 | /* `XHTML, HTML4, HTML5 Reset
2 | ----------------------------------------------------------------------------------------------------*/
3 |
4 | a,
5 | abbr,
6 | acronym,
7 | address,
8 | applet,
9 | article,
10 | aside,
11 | audio,
12 | b,
13 | big,
14 | blockquote,
15 | body,
16 | canvas,
17 | caption,
18 | center,
19 | cite,
20 | code,
21 | dd,
22 | del,
23 | details,
24 | dfn,
25 | dialog,
26 | div,
27 | dl,
28 | dt,
29 | em,
30 | embed,
31 | fieldset,
32 | figcaption,
33 | figure,
34 | font,
35 | footer,
36 | form,
37 | h1,
38 | h2,
39 | h3,
40 | h4,
41 | h5,
42 | h6,
43 | header,
44 | hgroup,
45 | hr,
46 | html,
47 | i,
48 | iframe,
49 | img,
50 | ins,
51 | kbd,
52 | label,
53 | legend,
54 | li,
55 | mark,
56 | menu,
57 | meter,
58 | nav,
59 | object,
60 | ol,
61 | output,
62 | p,
63 | pre,
64 | progress,
65 | q,
66 | rp,
67 | rt,
68 | ruby,
69 | s,
70 | samp,
71 | section,
72 | small,
73 | span,
74 | strike,
75 | strong,
76 | sub,
77 | summary,
78 | sup,
79 | table,
80 | tbody,
81 | td,
82 | tfoot,
83 | th,
84 | thead,
85 | time,
86 | tr,
87 | tt,
88 | u,
89 | ul,
90 | var,
91 | video,
92 | xmp {
93 | border: 0;
94 | margin: 0;
95 | padding: 0;
96 | font-size: 100%;
97 | }
98 |
99 | html,
100 | body {
101 | height: 100%;
102 | }
103 |
104 | article,
105 | aside,
106 | details,
107 | figcaption,
108 | figure,
109 | footer,
110 | header,
111 | hgroup,
112 | menu,
113 | nav,
114 | section {
115 | /*
116 | Override the default (display: inline) for
117 | browsers that do not recognize HTML5 tags.
118 |
119 | IE8 (and lower) requires a shiv:
120 | http://ejohn.org/blog/html5-shiv
121 | */
122 | display: block;
123 | }
124 |
125 | b,
126 | strong {
127 | /*
128 | Makes browsers agree.
129 | IE + Opera = font-weight: bold.
130 | Gecko + WebKit = font-weight: bolder.
131 | */
132 | font-weight: bold;
133 | }
134 |
135 | img {
136 | color: transparent;
137 | font-size: 0;
138 | vertical-align: middle;
139 | /*
140 | For IE.
141 | http://css-tricks.com/ie-fix-bicubic-scaling-for-images
142 | */
143 | -ms-interpolation-mode: bicubic;
144 | }
145 |
146 | li {
147 | /*
148 | For IE6 + IE7.
149 | */
150 | display: list-item;
151 | }
152 |
153 | table {
154 | border-collapse: collapse;
155 | border-spacing: 0;
156 | }
157 |
158 | th,
159 | td,
160 | caption {
161 | font-weight: normal;
162 | vertical-align: top;
163 | text-align: right;
164 | }
165 |
166 | q {
167 | quotes: none;
168 | }
169 |
170 | q:before,
171 | q:after {
172 | content: '';
173 | content: none;
174 | }
175 |
176 | sub,
177 | sup,
178 | small {
179 | font-size: 75%;
180 | }
181 |
182 | sub,
183 | sup {
184 | line-height: 0;
185 | position: relative;
186 | vertical-align: baseline;
187 | }
188 |
189 | sub {
190 | bottom: -0.25em;
191 | }
192 |
193 | sup {
194 | top: -0.5em;
195 | }
196 |
197 | svg {
198 | /*
199 | For IE9.
200 | */
201 | overflow: hidden;
202 | }
--------------------------------------------------------------------------------
/samples/src/auth_demo.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- auth_cb -- Authentication callback examples
3 | -- Copyright (C) 2013, 2020 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 | with Util.Properties;
8 | with Util.Log.Loggers;
9 | with Util.Http.Clients.AWS;
10 |
11 | with AWS.Config;
12 | with AWS.Config.Set;
13 | with AWS.Server;
14 | with AWS.Services.Dispatchers.URI;
15 | with AWS.Services.Page_Server;
16 | with AWS.Services.Web_Block.Registry;
17 | with AWS.Net.SSL;
18 |
19 | with Auth_CB;
20 |
21 | procedure Auth_Demo is
22 |
23 | Log : constant Util.Log.Loggers.Logger := Util.Log.Loggers.Create ("Auth_Demo");
24 |
25 | Dispatcher : AWS.Services.Dispatchers.URI.Handler;
26 | WS : AWS.Server.HTTP;
27 | Config : AWS.Config.Object;
28 | begin
29 | if not AWS.Net.SSL.Is_Supported then
30 | Log.Error ("SSL is not supported by AWS.");
31 | Log.Error ("SSL is required for the OpenID connector to connect to OpenID providers.");
32 | Log.Error ("Please, rebuild AWS with SSL support.");
33 | return;
34 | end if;
35 |
36 | -- Get the authentication provider configuration. We use the Util.Properties and some
37 | -- java like property file. Other configuration implementation are possible.
38 | Auth_CB.Config.Load_Properties ("samples.properties");
39 | Util.Log.Loggers.Initialize (Util.Properties.Manager (Auth_CB.Config));
40 |
41 | -- Setup the HTTP client implementation to use AWS.
42 | Util.Http.Clients.AWS.Register;
43 |
44 | -- Setup AWS dispatchers.
45 | AWS.Services.Dispatchers.URI.Register (Dispatcher, "/atlas/auth/auth",
46 | Auth_CB.Get_Authorization'Access,
47 | Prefix => True);
48 | AWS.Services.Dispatchers.URI.Register (Dispatcher, "/verify",
49 | Auth_CB.Verify_Authorization'Access);
50 | AWS.Services.Dispatchers.URI.Register (Dispatcher, "/atlas",
51 | AWS.Services.Page_Server.Callback'Access,
52 | Prefix => True);
53 | AWS.Services.Dispatchers.URI.Register (Dispatcher, "/success",
54 | Auth_CB.User_Info'Access);
55 | AWS.Services.Web_Block.Registry.Register ("success", "samples/web/success.thtml", null);
56 |
57 | -- Configure AWS.
58 | Config := AWS.Config.Get_Current;
59 | AWS.Config.Set.Session (Config, True);
60 | AWS.Config.Set.Session_Name (Config, "AUTH_DEMO");
61 | AWS.Config.Set.Reuse_Address (Config, True);
62 | AWS.Config.Set.WWW_Root (Config, "samples/web");
63 |
64 | AWS.Server.Start (WS, Dispatcher => Dispatcher, Config => Config);
65 |
66 | Log.Info ("Connect you browser to: http://localhost:8080/atlas/login.html");
67 | Log.Info ("Press 'q' key to stop the server.");
68 |
69 | AWS.Server.Wait (AWS.Server.Q_Key_Pressed);
70 |
71 | Log.Info ("Shutting down server...");
72 | AWS.Server.Shutdown (WS);
73 | end Auth_Demo;
74 |
--------------------------------------------------------------------------------
/regtests/src/security-random-tests.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-random-tests - Tests for random package
3 | -- Copyright (C) 2017, 2018, 2022, 2023 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Ada.Strings.Unbounded;
9 |
10 | with Util.Test_Caller;
11 | package body Security.Random.Tests is
12 |
13 | package Caller is new Util.Test_Caller (Test, "Security.Random");
14 |
15 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite) is
16 | begin
17 | Caller.Add_Test (Suite, "Test Security.Random.Generate",
18 | Test_Generate'Access);
19 | Caller.Add_Test (Suite, "Test Security.Random.Generate_String",
20 | Test_Generate_String'Access);
21 | end Add_Tests;
22 |
23 | -- ------------------------------
24 | -- Test Yadis discovery using static files
25 | -- ------------------------------
26 | procedure Test_Generate (T : in out Test) is
27 | use Ada.Strings.Unbounded;
28 |
29 | G : Generator;
30 | Max : constant Ada.Streams.Stream_Element_Offset := 10;
31 | begin
32 | for I in 1 .. Max loop
33 | declare
34 | use type Ada.Streams.Stream_Element;
35 | S : Ada.Streams.Stream_Element_Array (1 .. I)
36 | := (others => 0);
37 | Rand : Ada.Strings.Unbounded.Unbounded_String;
38 | begin
39 | -- Try 5 times to fill the array with random patterns and make sure
40 | -- we don't get any 0.
41 | for Retry in 1 .. 5 loop
42 | G.Generate (S);
43 | exit when (for all R of S => R /= 0);
44 | end loop;
45 | T.Assert ((for all R of S => R /= 0), "Generator failed to initialize all bytes");
46 |
47 | G.Generate (Positive (I), Rand);
48 | T.Assert (Length (Rand) > 0, "Generator failed to produce a base64url sequence");
49 | end;
50 | end loop;
51 | end Test_Generate;
52 |
53 | procedure Test_Generate_String (T : in out Test) is
54 | G : Generator;
55 | begin
56 | declare
57 | S1 : constant String := G.Generate (Bits => 256);
58 | S2 : constant String := G.Generate (Bits => 256);
59 | begin
60 | Util.Tests.Assert_Equals (T, 43, Natural (S1'Length),
61 | "Generated string 1 is too small");
62 | Util.Tests.Assert_Equals (T, 43, Natural (S2'Length),
63 | "Generated string 2 is too small");
64 | T.Assert (S1 /= S2, "Generated string are equal");
65 | end;
66 | for I in 1 .. 100 loop
67 | declare
68 | S1 : String (1 .. I) := (others => ' ');
69 | S2 : String (1 .. I) := (others => ' ');
70 | begin
71 | G.Generate (S1);
72 | G.Generate (S2);
73 | T.Assert (S1 /= S2, "Generated string are equal");
74 | end;
75 | end loop;
76 | end Test_Generate_String;
77 |
78 | end Security.Random.Tests;
79 |
--------------------------------------------------------------------------------
/src/security-auth-oauth.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-auth-oauth -- OAuth based authentication
3 | -- Copyright (C) 2013, 2020 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Security.OAuth.Clients;
9 | private package Security.Auth.OAuth is
10 |
11 | -- ------------------------------
12 | -- OAuth Manager
13 | -- ------------------------------
14 | -- The Manager provides the core operations for the OAuth authorization process.
15 | type Manager is abstract new Security.Auth.Manager with private;
16 |
17 | -- Initialize the authentication realm.
18 | overriding
19 | procedure Initialize (Realm : in out Manager;
20 | Params : in Parameters'Class;
21 | Provider : in String := PROVIDER_OPENID);
22 |
23 | -- Discover the OpenID provider that must be used to authenticate the user.
24 | -- The Name can be an URL or an alias that identifies the provider.
25 | -- A cached OpenID provider can be returned.
26 | -- Read the XRDS document from the URI and initialize the OpenID provider end point.
27 | -- (See OpenID Section 7.3 Discovery)
28 | overriding
29 | procedure Discover (Realm : in out Manager;
30 | Name : in String;
31 | Result : out End_Point);
32 |
33 | -- Associate the application (relying party) with the OpenID provider.
34 | -- The association can be cached.
35 | -- (See OpenID Section 8 Establishing Associations)
36 | overriding
37 | procedure Associate (Realm : in out Manager;
38 | OP : in End_Point;
39 | Result : out Association);
40 |
41 | -- Get the authentication URL to which the user must be redirected for authentication
42 | -- by the authentication server.
43 | overriding
44 | function Get_Authentication_URL (Realm : in Manager;
45 | OP : in End_Point;
46 | Assoc : in Association) return String;
47 |
48 | -- Verify the authentication result
49 | overriding
50 | procedure Verify (Realm : in out Manager;
51 | Assoc : in Association;
52 | Request : in Parameters'Class;
53 | Result : out Authentication);
54 |
55 | -- Verify the OAuth access token and retrieve information about the user.
56 | procedure Verify_Access_Token (Realm : in Manager;
57 | Assoc : in Association;
58 | Request : in Parameters'Class;
59 | Token : in Security.OAuth.Clients.Access_Token_Access;
60 | Result : in out Authentication) is abstract;
61 |
62 | private
63 |
64 | type Manager is abstract new Security.Auth.Manager with record
65 | Return_To : Unbounded_String;
66 | Realm : Unbounded_String;
67 | Scope : Unbounded_String;
68 | Issuer : Unbounded_String;
69 | App : Security.OAuth.Clients.Application;
70 | end record;
71 |
72 | end Security.Auth.OAuth;
73 |
--------------------------------------------------------------------------------
/docs/Security_OAuth_Servers.md:
--------------------------------------------------------------------------------
1 | ## OAuth Server
2 | OAuth server side is provided by the Security.OAuth.Servers package.
3 | This package allows to implement the authorization framework described in RFC 6749
4 | "The OAuth 2.0 Authorization Framework".
5 |
6 | The authorization method produces a Grant_Type object that contains the result
7 | of the grant (successful or denied). It is the responsibility of the caller to format
8 | the result in JSON/XML and return it to the client.
9 |
10 | Three important operations are defined for the OAuth 2.0 framework. They will be used
11 | in the following order:
12 |
13 | Authorize is used to obtain an authorization request. This operation is
14 | optional in the OAuth 2.0 framework since some authorization method directly return
15 | the access token. This operation is used by the "Authorization Code Grant" and the
16 | "Implicit Grant".
17 |
18 | Token is used to get the access token and optional refresh token. Each time it
19 | is called, a new token is generated.
20 |
21 | Authenticate is used for the API request to verify the access token
22 | and authenticate the API call. This operation can be called several times with the same
23 | token until the token is revoked or it has expired.
24 |
25 | Several grant types are supported.
26 |
27 | ### Application Manager
28 | The application manager maintains the repository of applications which are known by
29 | the server and which can request authorization. Each application is identified by
30 | a client identifier (represented by the client_id request parameter).
31 | The application defines the authorization methods which are allowed as well as
32 | the parameters to control and drive the authorization. This includes the redirection
33 | URI, the application secret, the expiration delay for the access token.
34 |
35 | The application manager is implemented by the application server and it must
36 | implement the Application_Manager interface with the Find_Application
37 | method. The Find_Application is one of the first call made during the
38 | authenticate and token generation phases.
39 |
40 | ### Resource Owner Password Credentials Grant
41 | The password grant is one of the easiest grant method to understand but it is also one
42 | of the less secure. In this grant method, the username and user password are passed in
43 | the request parameter together with the application client identifier. The realm verifies
44 | the username and password and when they are correct it generates the access token with
45 | an optional refresh token. The realm also returns in the grant the user principal that
46 | identifies the user.
47 |
48 | ```Ada
49 | Realm : Security.OAuth.Servers.Auth_Manager;
50 | Grant : Security.OAuth.Servers.Grant_Type;
51 | Realm.Token (Params, Grant);
52 |
53 | ```
54 |
55 | ### Accessing Protected Resources
56 | When accessing a protected resource, the API implementation will use the
57 | Authenticate operation to verify the access token and get a security principal.
58 | The security principal will identify the resource owner as well as the application
59 | that is doing the call.
60 |
61 | ```Ada
62 | Realm : Security.OAuth.Servers.Auth_Manager;
63 | Grant : Security.OAuth.Servers.Grant_Type;
64 | Token : String := ...;
65 | Realm.Authenticate (Token, Grant);
66 |
67 | ```
68 |
69 | When a security principal is returned, the access token was validated and the
70 | request is granted for the application.
71 |
72 |
73 |
--------------------------------------------------------------------------------
/samples.properties:
--------------------------------------------------------------------------------
1 | openid.realm=http://localhost:8080/verify
2 | openid.callback_url=http://localhost:8080/verify
3 | openid.success_url=http://localhost:8080/success
4 | auth.url.orange=https://openid.orange.fr
5 | auth.url.yahoo=https://api.login.yahoo.com/oauth2/request_auth
6 | auth.url.facebook=https://www.facebook.com/dialog/oauth
7 | auth.url.google-plus=https://accounts.google.com/o/oauth2/auth
8 | auth.url.gitlab=https://gitlab.com/oauth/authorize
9 | auth.url.github=https://github.com/login/oauth/authorize
10 | auth.url.twitter=https://api.twitter.com/oauth/authenticate
11 | auth.provider.orange=openid
12 | auth.provider.google=openid
13 | auth.provider.yahoo=yahoo
14 | auth.provider.twitter=yahoo
15 | auth.provider.gitlab=yahoo
16 | auth.provider.github=github
17 | auth.provider.facebook=facebook
18 | auth.provider.google-plus=google-plus
19 |
20 | # Get an account on Facebook
21 | facebook.client_id=PUT-HERE-YOUR-FACEBOOK-CLIENT-ID
22 | facebook.secret=PUT-HERE-YOUR-FACEBOOK-API-SECRET
23 | facebook.callback=https://demo.vacs.fr/oauth/localhost:8080/verify
24 | facebook.callback_url=https://demo.vacs.fr/oauth/localhost:8080/verify
25 | facebook.request_url=https://graph.facebook.com/oauth/access_token
26 | facebook.scope=read_stream,email
27 |
28 | # Get an account on Google+
29 | google-plus.issuer=accounts.google.com
30 | google-plus.client_id=PUT-HERE-YOUR-GOOGLE-CLIENT-ID
31 | google-plus.secret=PUT-HERE-YOUR-GOOGLE-API-SECRET
32 | google-plus.callback=https://demo.vacs.fr/oauth/localhost:8080/verify
33 | google-plus.callback_url=https://demo.vacs.fr/oauth/localhost:8080/verify
34 | google-plus.request_url=https://accounts.google.com/o/oauth2/token
35 | google-plus.scope=openid profile email
36 |
37 | # Yahoo! OpenId connect
38 | yahoo.issuer=https://api.login.yahoo.com
39 | yahoo.request_url=https://api.login.yahoo.com/oauth2/get_token
40 | yahoo.client_id=PUT-HERE-YOUR-YAHOO-CLIENT-ID
41 | yahoo.secret=PUT-HERE-YOUR-YAHOO-API-SECRET
42 | yahoo.app_id=PUT-HERE-YOUR-YAHOO-APP-ID
43 | yahoo.callback_url=https://demo.vacs.fr/oauth/localhost:8080/verify
44 | yahoo.scope=openid,profile,email
45 |
46 | # Yahoo! OpenId connect
47 | twitter.issuer=https://api.login.yahoo.com
48 | twitter.request_url=https://api.login.yahoo.com/oauth2/get_token
49 | twitter.client_id=
50 | twitter.secret=
51 | twitter.callback_url=https://demo.vacs.fr/oauth/localhost:8080/verify
52 | twitter.scope=openid,profile,email
53 |
54 | gitlab.issuer=https://gitlab.com
55 | gitlab.request_url=https://gitlab.com/oauth/token
56 | gitlab.client_id=PUT-HERE-YOUR-GITLAB-CLIENT-ID
57 | gitlab.secret=PUT-HERE-YOUR-GITLAB-SECRET
58 | gitlab.callback_url=https://demo.vacs.fr/oauth/localhost:8080/verify
59 | gitlab.scope=openid email
60 |
61 | github.issuer=https://github.com
62 | github.request_url=https://github.com/login/oauth/access_token
63 | github.client_id=PUT-HERE-YOUR-GITHUB-CLIENT-ID
64 | github.secret=PUT-HERE-YOUR-GITHUB-SECRET
65 | github.callback_url=https://demo.vacs.fr/oauth/localhost:8080/verify
66 | github.scope=openid
67 |
68 | # Configuration for log4j
69 | log4j.rootCategory=DEBUG,console,result
70 | log4j.appender.console=Console
71 | log4j.appender.console.level=DEBUG
72 | log4j.appender.console.layout=level-message
73 | log4j.appender.result=File
74 | log4j.appender.result.File=samples.log
75 |
76 | # Logger configuration
77 | log4j.logger.log=WARN
78 | log4j.logger.Util.Properties=DEBUG
79 | log4j.logger.Util.Log=DEBUG
80 | log4j.logger.Util=DEBUG
81 | log4j.logger.Util.Serialize.Mappers=WARN
82 | log4j.logger.Util.Serialize.IO=INFO
83 | log4j.logger.Security=DEBUG
84 |
--------------------------------------------------------------------------------
/src/security-permissions.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-permissions -- Definition of permissions
3 | -- Copyright (C) 2010, 2011, 2012, 2016, 2017 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 | private with Interfaces;
8 |
9 | -- == Permission ==
10 | -- The Security.Permissions package defines the different permissions that can be
11 | -- checked by the access control manager. An application should declare each permission
12 | -- by instantiating the Definition package:
13 | --
14 | -- package Perm_Create_Workspace is new Security.Permissions.Definition ("create-workspace");
15 | --
16 | -- This declares a permission that can be represented by "create-workspace " in
17 | -- configuration files. In Ada, the permission is used as follows:
18 | --
19 | -- Perm_Create_Workspace.Permission
20 | --
21 | package Security.Permissions is
22 |
23 | Invalid_Name : exception;
24 |
25 | -- Max number of permissions supported by the implementation.
26 | MAX_PERMISSION : constant Natural := 255;
27 |
28 | type Permission_Index is new Natural range 0 .. MAX_PERMISSION;
29 |
30 | type Permission_Index_Array is array (Positive range <>) of Permission_Index;
31 |
32 | NONE : constant Permission_Index := Permission_Index'First;
33 |
34 | -- Get the permission index associated with the name.
35 | function Get_Permission_Index (Name : in String) return Permission_Index;
36 |
37 | -- Get the list of permissions whose name is given in the string with separated comma.
38 | function Get_Permission_Array (List : in String) return Permission_Index_Array;
39 |
40 | -- Get the permission name given the index.
41 | function Get_Name (Index : in Permission_Index) return String;
42 |
43 | -- The permission root class.
44 | -- Each permission is represented by a Permission_Index number to provide a fast
45 | -- and efficient permission check.
46 | type Permission (Id : Permission_Index) is tagged limited null record;
47 |
48 | generic
49 | Name : String;
50 | package Definition is
51 | function Permission return Permission_Index;
52 | pragma Inline_Always (Permission);
53 | end Definition;
54 |
55 | -- Add the permission name and allocate a unique permission index.
56 | procedure Add_Permission (Name : in String;
57 | Index : out Permission_Index);
58 |
59 | type Permission_Index_Set is private;
60 |
61 | -- Check if the permission index set contains the given permission index.
62 | function Has_Permission (Set : in Permission_Index_Set;
63 | Index : in Permission_Index) return Boolean;
64 |
65 | -- Add the permission index to the set.
66 | procedure Add_Permission (Set : in out Permission_Index_Set;
67 | Index : in Permission_Index);
68 |
69 | -- Get the last permission index registered in the global permission map.
70 | function Get_Last_Permission_Index return Permission_Index;
71 |
72 | -- The empty set of permission indexes.
73 | EMPTY_SET : constant Permission_Index_Set;
74 |
75 | private
76 |
77 | INDEX_SET_SIZE : constant Natural := (MAX_PERMISSION + 7) / 8;
78 |
79 | type Permission_Index_Set is array (0 .. INDEX_SET_SIZE - 1) of Interfaces.Unsigned_8;
80 |
81 | EMPTY_SET : constant Permission_Index_Set := (others => 0);
82 |
83 | end Security.Permissions;
84 |
--------------------------------------------------------------------------------
/src/security-auth-oauth-googleplus.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-auth-oauth-googleplus -- Google+ OAuth based authentication
3 | -- Copyright (C) 2013, 2020 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Log.Loggers;
9 | with Security.OAuth.JWT;
10 | package body Security.Auth.OAuth.Googleplus is
11 |
12 | use Util.Log;
13 |
14 | Log : constant Loggers.Logger := Util.Log.Loggers.Create ("Security.Auth.OAuth.Googleplus");
15 |
16 | -- ------------------------------
17 | -- Verify the OAuth access token and retrieve information about the user.
18 | -- ------------------------------
19 | overriding
20 | procedure Verify_Access_Token (Realm : in Manager;
21 | Assoc : in Association;
22 | Request : in Parameters'Class;
23 | Token : in Security.OAuth.Clients.Access_Token_Access;
24 | Result : in out Authentication) is
25 | pragma Unreferenced (Request);
26 | begin
27 | -- The token returned by Google+ must contain an id_token.
28 | if not (Token.all in Security.OAuth.Clients.OpenID_Token'Class) then
29 | Log.Warn ("Invalid token instance created: missing the id_token");
30 | Set_Result (Result, INVALID_SIGNATURE, "invalid access token returned");
31 | return;
32 | end if;
33 |
34 | -- The id_token is a JWT token that must be decoded and verified.
35 | -- See https://developers.google.com/accounts/docs/OAuth2Login#validatingtoken
36 | -- It contains information to identify the user.
37 | declare
38 | T : constant Security.OAuth.Clients.OpenID_Token_Access :=
39 | Security.OAuth.Clients.OpenID_Token'Class (Token.all)'Access;
40 | Info : constant Security.OAuth.JWT.Token := Security.OAuth.JWT.Decode (T.Get_Id_Token);
41 | begin
42 | -- Verify that the JWT token concerns our application.
43 | if Security.OAuth.JWT.Get_Audience (Info) /= Realm.App.Get_Application_Identifier then
44 | Set_Result (Result, INVALID_SIGNATURE,
45 | "the access token was granted for another application");
46 | return;
47 |
48 | -- Verify that the issuer is Google+
49 | elsif Security.OAuth.JWT.Get_Issuer (Info) /= Realm.Issuer then
50 | Set_Result (Result, INVALID_SIGNATURE,
51 | "the access token was not generated by the expected authority");
52 | return;
53 |
54 | -- Verify that the nonce we received matches our nonce.
55 | elsif Security.OAuth.JWT.Get_Claim (Info, "nonce") /= Assoc.Nonce then
56 | Set_Result (Result, INVALID_SIGNATURE,
57 | "the access token was not generated with the expected nonce");
58 | return;
59 |
60 | end if;
61 | Result.Identity := To_Unbounded_String ("https://accounts.google.com/");
62 | Append (Result.Identity, Security.OAuth.JWT.Get_Subject (Info));
63 | Result.Claimed_Id := Result.Identity;
64 |
65 | -- The email is optional and depends on the scope.
66 | Result.Email := To_Unbounded_String (Security.OAuth.JWT.Get_Claim (Info, "email", ""));
67 | Set_Result (Result, AUTHENTICATED, "authenticated");
68 | end;
69 | end Verify_Access_Token;
70 |
71 | end Security.Auth.OAuth.Googleplus;
72 |
--------------------------------------------------------------------------------
/src/security-oauth-jwt.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-oauth-jwt -- OAuth Java Web Token
3 | -- Copyright (C) 2013 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Ada.Calendar;
9 | with Util.Properties;
10 |
11 | -- === JSON Web Token ===
12 | -- JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred
13 | -- between two parties. A JWT token is returned by an authorization server. It contains
14 | -- useful information that allows to verify the authentication and identify the user.
15 | --
16 | -- The Security.OAuth.JWT package implements the decoding part of JWT defined in:
17 | -- JSON Web Token (JWT), http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-07
18 | --
19 | -- A list of pre-defined ID tokens are returned in the JWT token claims and used for
20 | -- the OpenID Connect. This is specified in
21 | -- OpenID Connect Basic Client Profile 1.0 - draft 26,
22 | -- http://openid.net/specs/openid-connect-basic-1_0.html
23 | --
24 | package Security.OAuth.JWT is
25 |
26 | -- Exception raised if the encoded token is invalid or cannot be decoded.
27 | Invalid_Token : exception;
28 |
29 | type Token is private;
30 |
31 | -- Get the issuer claim from the token (the "iss" claim).
32 | function Get_Issuer (From : in Token) return String;
33 |
34 | -- Get the subject claim from the token (the "sub" claim).
35 | function Get_Subject (From : in Token) return String;
36 |
37 | -- Get the audience claim from the token (the "aud" claim).
38 | function Get_Audience (From : in Token) return String;
39 |
40 | -- Get the expiration claim from the token (the "exp" claim).
41 | function Get_Expiration (From : in Token) return Ada.Calendar.Time;
42 |
43 | -- Get the not before claim from the token (the "nbf" claim).
44 | function Get_Not_Before (From : in Token) return Ada.Calendar.Time;
45 |
46 | -- Get the issued at claim from the token (the "iat" claim).
47 | -- This is the time when the JWT was issued.
48 | function Get_Issued_At (From : in Token) return Ada.Calendar.Time;
49 |
50 | -- Get the authentication time claim from the token (the "auth_time" claim).
51 | function Get_Authentication_Time (From : in Token) return Ada.Calendar.Time;
52 |
53 | -- Get the JWT ID claim from the token (the "jti" claim).
54 | function Get_JWT_ID (From : in Token) return String;
55 |
56 | -- Get the authorized clients claim from the token (the "azp" claim).
57 | function Get_Authorized_Presenters (From : in Token) return String;
58 |
59 | -- Get the claim with the given name from the token.
60 | function Get_Claim (From : in Token;
61 | Name : in String;
62 | Default : in String := "") return String;
63 |
64 | -- Decode a string representing an encoded JWT token according to the JWT specification:
65 | --
66 | -- Section 7. Rules for Creating and Validating a JWT
67 | --
68 | -- The JWT token is composed of 3 parts encoded in Base64url and separated by '.' .
69 | -- The first part represents the header, the second part the claims and the last part
70 | -- the signature. The Decode operation splits the parts, decodes them,
71 | -- parses the JSON content represented by the header and the claims.
72 | -- The Decode operation does not verify the signature (yet!).
73 | --
74 | -- Return the decoded token or raise an exception.
75 | function Decode (Content : in String) return Token;
76 |
77 | private
78 |
79 | type Claims is new Util.Properties.Manager with null record;
80 |
81 | type Token is record
82 | Header : Util.Properties.Manager;
83 | Claims : Util.Properties.Manager;
84 | end record;
85 |
86 | end Security.OAuth.JWT;
87 |
--------------------------------------------------------------------------------
/src/security-auth-oauth-yahoo.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-auth-oauth-yahoo -- Yahoo! OAuth based authentication
3 | -- Copyright (C) 2013, 2014, 2020 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Log.Loggers;
9 | with Security.OAuth.JWT;
10 | package body Security.Auth.OAuth.Yahoo is
11 |
12 | use Util.Log;
13 | use Security.OAuth;
14 |
15 | Log : constant Loggers.Logger := Util.Log.Loggers.Create ("Security.Auth.OAuth.Yahoo");
16 |
17 | -- ------------------------------
18 | -- Verify the OAuth access token and retrieve information about the user.
19 | -- ------------------------------
20 | overriding
21 | procedure Verify_Access_Token (Realm : in Manager;
22 | Assoc : in Association;
23 | Request : in Parameters'Class;
24 | Token : in Security.OAuth.Clients.Access_Token_Access;
25 | Result : in out Authentication) is
26 | pragma Unreferenced (Request);
27 | begin
28 | -- The token returned by Yahoo! must contain an id_token.
29 | if not (Token.all in Security.OAuth.Clients.OpenID_Token'Class) then
30 | Log.Warn ("Invalid token instance created: missing the id_token");
31 | Set_Result (Result, INVALID_SIGNATURE, "invalid access token returned");
32 | return;
33 | end if;
34 |
35 | -- The id_token is a JWT token that must be decoded and verified.
36 | -- See https://developer.yahoo.com/oauth2/guide/openid_connect/decode_id_token.html
37 | -- It contains information to identify the user.
38 | declare
39 | T : constant Security.OAuth.Clients.OpenID_Token_Access :=
40 | Security.OAuth.Clients.OpenID_Token'Class (Token.all)'Access;
41 | Info : constant Security.OAuth.JWT.Token := Security.OAuth.JWT.Decode (T.Get_Id_Token);
42 | begin
43 | -- Verify that the JWT token concerns our application.
44 | if Security.OAuth.JWT.Get_Audience (Info) /= Realm.App.Get_Application_Identifier then
45 | Set_Result (Result, INVALID_SIGNATURE,
46 | "the access token was granted for another application");
47 | return;
48 |
49 | -- Verify that the issuer is Yahoo!
50 | elsif Security.OAuth.JWT.Get_Issuer (Info) /= Realm.Issuer then
51 | Set_Result (Result, INVALID_SIGNATURE,
52 | "the access token was not generated by the expected authority");
53 | return;
54 |
55 | -- Verify that the nonce we received matches our nonce.
56 | elsif Security.OAuth.JWT.Get_Claim (Info, "nonce") /= Assoc.Nonce then
57 | Set_Result (Result, INVALID_SIGNATURE,
58 | "the access token was not generated with the expected nonce");
59 | return;
60 |
61 | end if;
62 | Result.Identity := Realm.Issuer;
63 | Append (Result.Identity, "/");
64 | Append (Result.Identity, Security.OAuth.JWT.Get_Subject (Info));
65 | Result.Claimed_Id := Result.Identity;
66 | Result.First_Name := To_Unbounded_String (JWT.Get_Claim (Info, "name", ""));
67 | Result.Last_Name := To_Unbounded_String (JWT.Get_Claim (Info, "family_name", ""));
68 | Result.Gender := To_Unbounded_String (JWT.Get_Claim (Info, "gender", ""));
69 | Result.Nickname := To_Unbounded_String (JWT.Get_Claim (Info, "nickname", ""));
70 | Result.Full_Name := To_Unbounded_String (JWT.Get_Claim (Info, "name", ""));
71 |
72 | -- The email is optional and depends on the scope.
73 | Result.Email := To_Unbounded_String (JWT.Get_Claim (Info, "email", ""));
74 | Set_Result (Result, AUTHENTICATED, "authenticated");
75 | end;
76 | end Verify_Access_Token;
77 |
78 | end Security.Auth.OAuth.Yahoo;
79 |
--------------------------------------------------------------------------------
/src/security-auth-openid.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-openid -- OpenID 2.0 Support
3 | -- Copyright (C) 2009, 2010, 2011, 2012, 2018 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | -- == OpenID Configuration ==
9 | -- The Open ID provider needs the following configuration parameters:
10 | --
11 | -- openid.realm The OpenID realm parameter passed in the authentication URL.
12 | -- openid.callback_url The OpenID return_to parameter.
13 | --
14 | private package Security.Auth.OpenID is
15 |
16 | -- ------------------------------
17 | -- OpenID Manager
18 | -- ------------------------------
19 | -- The Manager provides the core operations for the OpenID process.
20 | type Manager is new Security.Auth.Manager with private;
21 |
22 | -- Initialize the OpenID authentication realm. Get the openid.realm
23 | -- and openid.callback_url parameters to configure the realm.
24 | overriding
25 | procedure Initialize (Realm : in out Manager;
26 | Params : in Parameters'Class;
27 | Name : in String := PROVIDER_OPENID);
28 |
29 | -- Discover the OpenID provider that must be used to authenticate the user.
30 | -- The Name can be an URL or an alias that identifies the provider.
31 | -- A cached OpenID provider can be returned.
32 | -- Read the XRDS document from the URI and initialize the OpenID provider end point.
33 | -- (See OpenID Section 7.3 Discovery)
34 | overriding
35 | procedure Discover (Realm : in out Manager;
36 | Name : in String;
37 | Result : out End_Point);
38 |
39 | -- Associate the application (relying party) with the OpenID provider.
40 | -- The association can be cached.
41 | -- (See OpenID Section 8 Establishing Associations)
42 | overriding
43 | procedure Associate (Realm : in out Manager;
44 | OP : in End_Point;
45 | Result : out Association);
46 |
47 | -- Get the authentication URL to which the user must be redirected for authentication
48 | -- by the authentication server.
49 | overriding
50 | function Get_Authentication_URL (Realm : in Manager;
51 | OP : in End_Point;
52 | Assoc : in Association) return String;
53 |
54 | -- Verify the authentication result
55 | overriding
56 | procedure Verify (Realm : in out Manager;
57 | Assoc : in Association;
58 | Request : in Parameters'Class;
59 | Result : out Authentication);
60 |
61 | -- Verify the authentication result
62 | procedure Verify_Discovered (Realm : in out Manager;
63 | Assoc : in Association;
64 | Request : in Parameters'Class;
65 | Result : out Authentication);
66 |
67 | -- Verify the signature part of the result
68 | procedure Verify_Signature (Realm : in Manager;
69 | Assoc : in Association;
70 | Request : in Parameters'Class;
71 | Result : in out Authentication);
72 |
73 | -- Extract from the XRDS content the OpenID provider URI.
74 | -- The default implementation is very basic as it returns the first
75 | -- available in the stream without validating the XRDS document.
76 | -- Raises the Invalid_End_Point exception if the URI cannot be found.
77 | procedure Extract_XRDS (Realm : in out Manager;
78 | Content : in String;
79 | Result : out End_Point);
80 |
81 | private
82 |
83 | type Manager is new Security.Auth.Manager with record
84 | Return_To : Unbounded_String;
85 | Realm : Unbounded_String;
86 | end record;
87 |
88 | end Security.Auth.OpenID;
89 |
--------------------------------------------------------------------------------
/src/security.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security -- Security
3 | -- Copyright (C) 2010, 2011, 2012, 2015, 2017, 2018, 2019 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | -- = Security =
9 | -- The `Security` package provides a security framework that allows
10 | -- an application to use OpenID or OAuth security frameworks. This security
11 | -- framework was first developed within the Ada Server Faces project.
12 | -- It was moved to a separate project so that it can easily be used with AWS.
13 | -- This package defines abstractions that are close or similar to Java
14 | -- security package.
15 | --
16 | -- The security framework uses the following abstractions:
17 | --
18 | -- * **Policy and policy manager**:
19 | -- The `Policy` defines and implements the set of security rules that specify how to
20 | -- protect the system or resources. The `Policy_Manager` maintains the security policies.
21 | --
22 | -- * **Principal**:
23 | -- The `Principal` is the entity that can be authenticated. A principal is obtained
24 | -- after successful authentication of a user or of a system through an authorization process.
25 | -- The OpenID or OAuth authentication processes generate such security principal.
26 | --
27 | -- * **Permission**:
28 | -- The `Permission` represents an access to a system or application resource.
29 | -- A permission is checked by using the security policy manager. The policy manager uses a
30 | -- security controller to enforce the permission.
31 | --
32 | -- The `Security_Context` holds the contextual information that the security controller
33 | -- can use to verify the permission. The security context is associated with a principal and
34 | -- a set of policy context.
35 | --
36 | -- == Overview ==
37 | -- An application will create a security policy manager and register one or several security
38 | -- policies (yellow). The framework defines a simple role based security policy and an URL
39 | -- security policy intended to provide security in web applications. The security policy manager
40 | -- reads some security policy configuration file which allows the security policies to configure
41 | -- and create the security controllers. These controllers will enforce the security according
42 | -- to the application security rules. All these components are built only once when
43 | -- an application starts.
44 | --
45 | -- A user is authenticated through an authentication system which creates a `Principal`
46 | -- instance that identifies the user (green). The security framework provides two authentication
47 | -- systems: OpenID and OAuth 2.0 OpenID Connect.
48 | --
49 | -- [images/ModelOverview.png]
50 | --
51 | -- When a permission must be enforced, a security context is created and linked to the
52 | -- `Principal` instance (blue). Additional security policy context can be added depending
53 | -- on the application context. To check the permission, the security policy manager is called
54 | -- and it will ask a security controller to verify the permission.
55 | --
56 | -- The framework allows an application to plug its own security policy, its own policy context,
57 | -- its own principal and authentication mechanism.
58 | --
59 | -- @include security-permissions.ads
60 | --
61 | -- == Principal ==
62 | -- A principal is created by using either the [Security_Auth OpenID],
63 | -- the [Security_OAuth OAuth] or another authentication mechanism. The authentication produces
64 | -- an object that must implement the `Principal` interface. For example:
65 | --
66 | -- P : Security.Auth.Principal_Access := Security.Auth.Create_Principal (Auth);
67 | --
68 | -- or
69 | --
70 | -- P : Security.OAuth.Clients.Access_Token_Access := Security.OAuth.Clients.Create_Access_Token
71 | --
72 | -- The principal is then stored in a security context.
73 | --
74 | -- @include security-contexts.ads
75 | --
76 | package Security is
77 |
78 | pragma Preelaborate;
79 |
80 | -- ------------------------------
81 | -- Principal
82 | -- ------------------------------
83 | type Principal is limited interface;
84 | type Principal_Access is access all Principal'Class;
85 |
86 | -- Get the principal name.
87 | function Get_Name (From : in Principal) return String is abstract;
88 |
89 | end Security;
90 |
--------------------------------------------------------------------------------
/samples/web/atlas/css/users.css:
--------------------------------------------------------------------------------
1 | /**
2 | * users.css - Style for users module
3 | * Copyright (C) 2011 Stephane Carrez
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | */
6 | div.awa-side1, div.awa-side2 {
7 | float: left;
8 | }
9 | div.awa-side1 {
10 | padding-right: 1%;
11 | }
12 | div.awa-side2 {
13 | padding-left: 1%;
14 | width: 48%;
15 | }
16 | li.message {
17 | height: 30px;
18 | overflow: hidden;
19 | }
20 |
21 | div.awa-login h2 {
22 | float: left;
23 | width: 96%;
24 | background: white;
25 | padding: 2%;
26 | -moz-border-radius-topleft: 6px;
27 | -moz-border-radius-topright: 6px;
28 | -webkit-border-top-right-radius: 6px;
29 | -webkit-border-top-left-radius: 6px;
30 | }
31 | div.awa-login {
32 | border: 2px solid #A6C9E2;
33 | background: none repeat scroll 0 0 #F9FDFF;
34 | display: block;
35 | float: left;
36 | width: 98%;
37 | margin: 5px;
38 | -moz-border-radius: 6px;
39 | -webkit-border-radius: 6px 6px;
40 | }
41 | div.awa-lost-password {
42 | width: 50%;
43 | }
44 | div.awa-login p {
45 | width: 96%;
46 | padding-left: 2%;
47 | padding-right: 2%;
48 | padding-top: 5px;
49 | display: block;
50 | float: left;
51 | }
52 | .awa-login ul, #awa-message-list {
53 | list-style-type: none;
54 | margin: 1em 0;
55 | float: left;
56 | width: 96%;
57 | padding-left: 2%;
58 | padding-right: 2%;
59 | }
60 |
61 | .awa-login #login-button, .awa-login #lost-password-button, .awa-login #register-button {
62 | margin-left: 10%;
63 | margin-top: 15px;
64 | width: 200px;
65 | }
66 | .awa-login #lost-password {
67 | clear: both;
68 | display: block;
69 | margin-top: 10px;
70 | float: right;
71 | }
72 | .awa-login #signup-link {
73 | clear: both;
74 | display: block;
75 | float: right;
76 | }
77 |
78 | ul.openid-login li {
79 | float: left;
80 | padding-left: 5px;
81 | }
82 | div.openid-login {
83 | float: left;
84 | border: 1px solid #ccc;
85 | margin-left: 10px;
86 | margin-right: 10px;
87 | margin: 5px;
88 | }
89 |
90 | ul.openid-login a {
91 | display: block;
92 | height: 35px;
93 | text-align: center;
94 | line-height: 35px;
95 | border: 1px solid #ccc;
96 | }
97 |
98 | a.openid-orange em,
99 | a.openid-yahoo em,
100 | a.openid-facebook em,
101 | a.openid-google-plus em,
102 | a.openid-gitlab em,
103 | a.openid-github em,
104 | a.openid-google em {
105 | display: block;
106 | float: left;
107 | background: url(../images/open-id-logos-1.png);
108 | width: 119px;
109 | height: 40px;
110 | overflow: hidden;
111 | }
112 | a.openid-yahoo em {
113 | background-position: 0 -39px;
114 | height: 25px;
115 | }
116 | div.openid-login a.openid-orange {
117 | padding-left: 0;
118 | }
119 | a.openid-orange em {
120 | background-position: 0 -242px;
121 | height: 35px;
122 | }
123 | a.openid-facebook em {
124 | margin-top: 3px;
125 | margin-left: 3px;
126 | background-position: 0 -280px;
127 | height: 33px;
128 | width: 40px;
129 | }
130 | a.openid-google-plus em {
131 | margin-left: 3px;
132 | margin-right: 10px;
133 | background-position: 0 -312px;
134 | height: 33px;
135 | width: 37px;
136 | border-right: 1px solid #bb3f30;
137 | }
138 | a.openid-github em {
139 | margin-left: 3px;
140 | margin-right: 10px;
141 | background-position: 0 -68px;
142 | height: 33px;
143 | width: 120px;
144 | }
145 | a.openid-gitlab em {
146 | margin-left: 3px;
147 | margin-right: 10px;
148 | background-position: 0 -348px;
149 | height: 33px;
150 | width: 120px;
151 | }
152 | a.openid-facebook {
153 | font-weight: bold;
154 | color: #424242;
155 | text-decoration: none;
156 | padding-right: 3px;
157 | }
158 | a.openid-gitlab {
159 | font-weight: bold;
160 | color: #424242;
161 | text-decoration: none;
162 | padding-right: 3px;
163 | }
164 | a.openid-github {
165 | font-weight: bold;
166 | color: #424242;
167 | text-decoration: none;
168 | padding-right: 3px;
169 | }
170 | a.openid-google-plus {
171 | font-weight: bold;
172 | color: #fff;
173 | text-decoration: none;
174 | padding-right: 3px;
175 | background-color: #dd4b39;
176 | }
177 | div.openid-info {
178 | float: left;
179 | padding: 20px;
180 | border: 1px solid #ccc;
181 | }
182 | div.openid-info dt {
183 | float: left;
184 | width: 100px;
185 | clear: left;
186 | }
187 | div.openid-info dd {
188 | float: left;
189 | padding-left: 20px;
190 | }
191 | div.openid-retry {
192 | float: left;
193 | clear: both;
194 | padding-top: 30px;
195 | }
196 |
--------------------------------------------------------------------------------
/samples/web/atlas/css/awa.css:
--------------------------------------------------------------------------------
1 | /**
2 | * main.css - Main style for AWA pages
3 | * Copyright (C) 2011, 2012 Stephane Carrez
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | */
6 | html, body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, textarea, p, blockquote, th, td, label {
7 | margin: 0;
8 | padding: 0;
9 | }
10 | html, body {
11 | height: 100%;
12 | }
13 | body {
14 | font-family: Arial, Verdana, sans-serif;
15 | font-size: 14px;
16 | background-color: #f0f0f0;
17 | overflow-y: scroll;
18 | }
19 | /* IE 6 min-height hack */
20 | * html #pam-page-content {
21 | height: 100%;
22 | }
23 | body.awa {
24 | background-color: #81CFB7;
25 | }
26 |
27 | #awa-page {
28 | position: relative;
29 | min-height:100%;
30 | margin-left: 1%;
31 | margin-right: 1%;
32 | background-color: white;
33 | margin-bottom: -30px; /* See #awa-clfooter height */
34 | display: block;
35 | width: 98%;
36 | }
37 | .awa #awa-page {
38 | background-color: #99DFCA;
39 | }
40 | #awa-hdr {
41 | position: absolute;
42 | padding: 0;
43 | margin: 0;
44 | width: 100%;
45 | height: 25px;
46 | background-color: #333; /* #9BCFBA; */
47 | border-bottom: 5px solid #333; /* #3E6F5C; */
48 | z-index: 1;
49 | }
50 | #awa-hdr ul {
51 | float: right;
52 | list-style-type: none;
53 | padding-right: 20px;
54 | }
55 | #awa-hdr li {
56 | float: right;
57 | padding-left: 5px;
58 | padding-right: 5px;
59 | line-height: 24px;
60 | font-size: 16px;
61 | }
62 | #awa-hdr li a, #awa-hdr li {
63 | color: #ddd;
64 | font-size: 12px;
65 | text-decoration: none;
66 | border-bottom: 3px solid #333;
67 | }
68 | #awa-hdr li a:hover {
69 | color: #fff;
70 | border-bottom: 3px solid orange;
71 | }
72 | #awa-hdr #awa-top-nav {
73 | float: left;
74 | }
75 | #awa-content {
76 | padding-top: 50px;
77 | }
78 | .awa-ok {
79 | float: left;
80 | width: 48px;
81 | height: 48px;
82 | margin-left: 5px;
83 | margin-right: 15px;
84 | }
85 | #awa-footer {
86 | float: none;
87 | width: 100%;
88 | position: relative;
89 | bottom: 0;
90 | height: 2px;
91 | }
92 | #awa-copyright {
93 | float: left;
94 | margin-left: 20px;
95 | margin-right: 5px;
96 | }
97 | #awa-clfooter {
98 | clear: both;
99 | height: 30px; /* See #awa-page margin bottom */
100 | width: 100%;
101 | }
102 | #awa-footer_content {
103 | float: right;
104 | margin-right: 20px;
105 | }
106 | ul.buttons, ul.awa-buttons {
107 | float: left;
108 | list-style-type: none;
109 | margin-top: 1em;
110 | margin-bottom: 1em;
111 | }
112 |
113 | li.awa-error label {
114 | color: red;
115 | }
116 |
117 | /**
118 | * AWA Errors
119 | */
120 | span.awa-error, dl.awa-error dt label {
121 | color: red;
122 | }
123 | span.awa-error {
124 | font-size: 14px;
125 | margin-bottom: 2px;
126 | margin-top: 2px;
127 | height: auto;
128 | }
129 |
130 | /**
131 | * Form definition.
132 | */
133 | div.awa-form {
134 | font-size: 16px;
135 | float: left;
136 | width: 100%;
137 | }
138 |
139 | /**
140 | * A form field (optional or required).
141 | * - dt contains the label
142 | * - dd contains the input field
143 | */
144 | dl.awa-optional, dl.awa-required {
145 | width: 96%;
146 | padding-left: 2%;
147 | padding-right: 2%;
148 | }
149 | dl.awa-optional dt, dl.awa-required dt {
150 | float: left;
151 | width: 100%;
152 | padding-top: 8px;
153 | }
154 | dl.awa-optional dd, dl.awa-required dd {
155 | float: left;
156 | clear: both;
157 | width: 100%;
158 | }
159 | dl.awa-error dt {
160 | color: red;
161 | }
162 | dl.awa-error span.error {
163 | font-size: 0.9em;
164 | }
165 | dl.awa-optional label, dl.awa-required label {
166 | display: inline;
167 | float: left;
168 | clear: left;
169 | text-align: left;
170 | color: #555;
171 | font-weight: bold;
172 | padding-right: 5px;
173 | }
174 | dl.awa-optional input, dl.awa-required input {
175 | width: 98%;
176 | font-size: 16px;
177 | padding: 3px;
178 | }
179 | dl.awa-optional textarea, dl.awa-required textarea {
180 | width: 98%;
181 | font-size: 16px;
182 | padding: 3px;
183 | }
184 |
185 | /**
186 | * Error messages
187 | */
188 | div.awa-messages {
189 | width: 96%;
190 | height: 43px;
191 | float: left;
192 | padding-top: 5px;
193 | padding-left: 2%;
194 | padding-right: 2%;
195 | }
196 |
197 | /**
198 | * A global error message.
199 | */
200 | div.awa-error {
201 | width: 640px;
202 | margin: auto;
203 | font-size: 16px;
204 | }
--------------------------------------------------------------------------------
/regtests/src/security-permissions-tests.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- Security-permissions-tests - Unit tests for Security.Permissions
3 | -- Copyright (C) 2011, 2012, 2016, 2018 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 | with Util.Test_Caller;
8 |
9 | package body Security.Permissions.Tests is
10 |
11 | package Caller is new Util.Test_Caller (Test, "Security.Permissions");
12 |
13 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite) is
14 | begin
15 | Caller.Add_Test (Suite, "Test Security.Permissions.Add_Permission",
16 | Test_Add_Permission'Access);
17 | Caller.Add_Test (Suite, "Test Security.Permissions.Get_Permission_Index",
18 | Test_Add_Permission'Access);
19 | Caller.Add_Test (Suite, "Test Security.Permissions.Definition",
20 | Test_Define_Permission'Access);
21 | Caller.Add_Test (Suite, "Test Security.Permissions.Get_Permission_Index (invalid name)",
22 | Test_Get_Invalid_Permission'Access);
23 | Caller.Add_Test (Suite, "Test Security.Permissions.Add_Permission (Set)",
24 | Test_Add_Permission_Set'Access);
25 | end Add_Tests;
26 |
27 | -- ------------------------------
28 | -- Test Add_Permission and Get_Permission_Index
29 | -- ------------------------------
30 | procedure Test_Add_Permission (T : in out Test) is
31 | Index1, Index2 : Permission_Index;
32 | begin
33 | Add_Permission ("test-create-permission", Index1);
34 |
35 | T.Assert (Index1 = Get_Permission_Index ("test-create-permission"),
36 | "Get_Permission_Index failed");
37 |
38 | Add_Permission ("test-create-permission", Index2);
39 |
40 | T.Assert (Index2 = Index1,
41 | "Add_Permission failed");
42 |
43 | end Test_Add_Permission;
44 |
45 | -- ------------------------------
46 | -- Test the permission created by the Definition package.
47 | -- ------------------------------
48 | procedure Test_Define_Permission (T : in out Test) is
49 | Index : Permission_Index;
50 | begin
51 | Index := Get_Permission_Index ("admin");
52 | T.Assert (P_Admin.Permission = Index, "Invalid permission for admin");
53 |
54 | Index := Get_Permission_Index ("create");
55 | T.Assert (P_Create.Permission = Index, "Invalid permission for create");
56 |
57 | Index := Get_Permission_Index ("update");
58 | T.Assert (P_Update.Permission = Index, "Invalid permission for update");
59 |
60 | Index := Get_Permission_Index ("delete");
61 | T.Assert (P_Delete.Permission = Index, "Invalid permission for delete");
62 |
63 | T.Assert (P_Admin.Permission /= P_Create.Permission, "Admin or create permission invalid");
64 | T.Assert (P_Admin.Permission /= P_Update.Permission, "Admin or update permission invalid");
65 | T.Assert (P_Admin.Permission /= P_Delete.Permission, "Admin or delete permission invalid");
66 | T.Assert (P_Create.Permission /= P_Update.Permission, "Create or update permission invalid");
67 | T.Assert (P_Create.Permission /= P_Delete.Permission, "Create or delete permission invalid");
68 | T.Assert (P_Update.Permission /= P_Delete.Permission, "Create or delete permission invalid");
69 | end Test_Define_Permission;
70 |
71 | -- ------------------------------
72 | -- Test Get_Permission on invalid permission name.
73 | -- ------------------------------
74 | procedure Test_Get_Invalid_Permission (T : in out Test) is
75 | Index : Permission_Index;
76 |
77 | pragma Unreferenced (Index);
78 | begin
79 | Index := Get_Permission_Index ("invalid");
80 | Util.Tests.Fail (T, "No exception raised by Get_Permission_Index for an invalid name");
81 |
82 | exception
83 | when Invalid_Name =>
84 | null;
85 | end Test_Get_Invalid_Permission;
86 |
87 | -- ------------------------------
88 | -- Test operations on the Permission_Index_Set.
89 | -- ------------------------------
90 | procedure Test_Add_Permission_Set (T : in out Test) is
91 | Set : Permission_Index_Set := EMPTY_SET;
92 | begin
93 | T.Assert (not Has_Permission (Set, P_Update.Permission),
94 | "The update permission is not in the set");
95 | Add_Permission (Set, P_Update.Permission);
96 | T.Assert (Has_Permission (Set, P_Update.Permission),
97 | "The update permission is in the set");
98 | end Test_Add_Permission_Set;
99 |
100 | end Security.Permissions.Tests;
101 |
--------------------------------------------------------------------------------
/src/security-oauth.ads:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-oauth -- OAuth Security
3 | -- Copyright (C) 2012, 2016, 2017, 2018, 2019, 2020 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 | with Ada.Strings.Unbounded;
8 |
9 | -- = OAuth =
10 | -- The Security.OAuth package defines and implements the OAuth 2.0 authorization
11 | -- framework as defined by the IETF working group in RFC 6749:
12 | -- The OAuth 2.0 Authorization Framework.
13 | --
14 | -- @include security-oauth-clients.ads
15 | -- @include security-oauth-servers.ads
16 | package Security.OAuth is
17 |
18 | -- OAuth 2.0: Section 10.2.2. Initial Registry Contents
19 | -- RFC 6749: 11.2.2. Initial Registry Contents
20 | CLIENT_ID : constant String := "client_id";
21 | CLIENT_SECRET : constant String := "client_secret";
22 | RESPONSE_TYPE : constant String := "response_type";
23 | REDIRECT_URI : constant String := "redirect_uri";
24 | SCOPE : constant String := "scope";
25 | STATE : constant String := "state";
26 | CODE : constant String := "code";
27 | ERROR_DESCRIPTION : constant String := "error_description";
28 | ERROR_URI : constant String := "error_uri";
29 | GRANT_TYPE : constant String := "grant_type";
30 | ACCESS_TOKEN : constant String := "access_token";
31 | TOKEN_TYPE : constant String := "token_type";
32 | EXPIRES_IN : constant String := "expires_in";
33 | USERNAME : constant String := "username";
34 | PASSWORD : constant String := "password";
35 | REFRESH_TOKEN : constant String := "refresh_token";
36 |
37 | NONCE_TOKEN : constant String := "nonce";
38 |
39 | -- RFC 6749: 5.2. Error Response
40 | INVALID_REQUEST : aliased constant String := "invalid_request";
41 | INVALID_CLIENT : aliased constant String := "invalid_client";
42 | INVALID_GRANT : aliased constant String := "invalid_grant";
43 | UNAUTHORIZED_CLIENT : aliased constant String := "unauthorized_client";
44 | UNSUPPORTED_GRANT_TYPE : aliased constant String := "unsupported_grant_type";
45 | INVALID_SCOPE : aliased constant String := "invalid_scope";
46 |
47 | -- RFC 6749: 4.1.2.1. Error Response
48 | ACCESS_DENIED : aliased constant String := "access_denied";
49 | UNSUPPORTED_RESPONSE_TYPE : aliased constant String := "unsupported_response_type";
50 | SERVER_ERROR : aliased constant String := "server_error";
51 | TEMPORARILY_UNAVAILABLE : aliased constant String := "temporarily_unavailable";
52 |
53 | type Client_Authentication_Type is (AUTH_NONE, AUTH_BASIC);
54 |
55 | -- ------------------------------
56 | -- Application
57 | -- ------------------------------
58 | -- The Application holds the necessary information to let a user
59 | -- grant access to its protected resources on the resource server. It contains
60 | -- information that allows the OAuth authorization server to identify the
61 | -- application (client id and secret key).
62 | type Application is tagged private;
63 |
64 | -- Get the application identifier.
65 | function Get_Application_Identifier (App : in Application) return String;
66 |
67 | -- Set the application identifier used by the OAuth authorization server
68 | -- to identify the application (for example, the App ID in Facebook).
69 | procedure Set_Application_Identifier (App : in out Application;
70 | Client : in String);
71 |
72 | -- Set the application secret defined in the OAuth authorization server
73 | -- for the application (for example, the App Secret in Facebook).
74 | procedure Set_Application_Secret (App : in out Application;
75 | Secret : in String);
76 |
77 | -- Set the redirection callback that will be used to redirect the user
78 | -- back to the application after the OAuth authorization is finished.
79 | procedure Set_Application_Callback (App : in out Application;
80 | URI : in String);
81 |
82 | -- Set the client authentication method used when doing OAuth calls for this application.
83 | -- See RFC 6749, 2.3. Client Authentication
84 | procedure Set_Client_Authentication (App : in out Application;
85 | Method : in Client_Authentication_Type);
86 |
87 | private
88 |
89 | type Application is tagged record
90 | Client_Id : Ada.Strings.Unbounded.Unbounded_String;
91 | Secret : Ada.Strings.Unbounded.Unbounded_String;
92 | Callback : Ada.Strings.Unbounded.Unbounded_String;
93 | Client_Auth : Client_Authentication_Type := AUTH_NONE;
94 | end record;
95 |
96 | end Security.OAuth;
97 |
--------------------------------------------------------------------------------
/regtests/src/security-oauth-jwt-tests.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- Security-oayth-jwt-tests - Unit tests for JSON Web Token
3 | -- Copyright (C) 2013, 2015 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 | with Ada.Calendar.Formatting;
8 |
9 | with Util.Test_Caller;
10 |
11 | package body Security.OAuth.JWT.Tests is
12 |
13 | package Caller is new Util.Test_Caller (Test, "Security.OAuth.JWT");
14 |
15 | -- A JWT token returned by Google+.
16 | K : constant String := "eyJhbGciOiJSUzI1NiIsImtpZCI6IjVmOTBlMWExMGE4YzgwZWJhZWNmYzM4NzBjZDl"
17 | & "lMGVhMGI3ZDVmZGMifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiYXRfaGFzaCI6Im9Ka19EYnFvb1"
18 | & "FVc0FhY3k2cnkxeHciLCJhdWQiOiI4NzI2NTU5OTQwMTQuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJz"
19 | & "dWIiOiIxMDgzNjA3MDMwOTk3MDg5Nzg4NzAiLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJhenAiOiI4NzI2NT"
20 | & "U5OTQwMTQuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJlbWFpbCI6InN0ZXBoYW5lLmNhcnJlekBnbWFp"
21 | & "bC5jb20iLCJpYXQiOjEzNjg5NjgyMzMsImV4cCI6MTM2ODk3MjEzM30.UL1qp2wmleV-ED2A_hlqgDLIGgJB3f"
22 | & "_N7fiz1CgttJcwbmMVwhag3ox2WE9C1KwXhrjwT8eigZ0WkDys5WO1dYs2G1QbDZPnsYYMyHK9XpycaDMEKtVZ"
23 | & "C4C6DkB1SrBHbN0Tv6ExWpszzp1JEL8nZnHd3T_AA3paqONnkvQw_yo";
24 |
25 | procedure Test_Operation (T : in out Test) is
26 | R : Token;
27 | begin
28 | R := Decode (K);
29 | Util.Tests.Assert_Equals (T, Value, Get (R), "Extraction failed");
30 | end Test_Operation;
31 |
32 | procedure Test_Time_Operation (T : in out Test) is
33 | R : Token;
34 | begin
35 | R := Decode (K);
36 | Util.Tests.Assert_Equals (T, Value, Ada.Calendar.Formatting.Image (Get (R)),
37 | "Extraction failed");
38 | end Test_Time_Operation;
39 |
40 | procedure Test_Get_Issuer is
41 | new Test_Operation (Get_Issuer, "accounts.google.com");
42 | procedure Test_Get_Audience is
43 | new Test_Operation (Get_Audience, "872655994014.apps.googleusercontent.com");
44 | procedure Test_Get_Subject is
45 | new Test_Operation (Get_Subject, "108360703099708978870");
46 | procedure Test_Get_Authorized_Presenters is
47 | new Test_Operation (Get_Authorized_Presenters, "872655994014.apps.googleusercontent.com");
48 | procedure Test_Get_Expiration is
49 | new Test_Time_Operation (Get_Expiration, "2013-05-19 14:02:13");
50 | procedure Test_Get_Issued_At is
51 | new Test_Time_Operation (Get_Issued_At, "2013-05-19 12:57:13");
52 |
53 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite) is
54 | begin
55 | Caller.Add_Test (Suite, "Test Security.OAuth.JWT.Decode",
56 | Test_Get_Issuer'Access);
57 | Caller.Add_Test (Suite, "Test Security.OAuth.JWT.Get_Issuer",
58 | Test_Get_Issuer'Access);
59 | Caller.Add_Test (Suite, "Test Security.OAuth.JWT.Get_Audience",
60 | Test_Get_Audience'Access);
61 | Caller.Add_Test (Suite, "Test Security.OAuth.JWT.Get_Subject",
62 | Test_Get_Subject'Access);
63 | Caller.Add_Test (Suite, "Test Security.OAuth.JWT.Get_Authorized_Presenters",
64 | Test_Get_Authorized_Presenters'Access);
65 | Caller.Add_Test (Suite, "Test Security.OAuth.JWT.Get_Expiration",
66 | Test_Get_Expiration'Access);
67 | Caller.Add_Test (Suite, "Test Security.OAuth.JWT.Get_Authentication_Time",
68 | Test_Get_Issued_At'Access);
69 | Caller.Add_Test (Suite, "Test Security.OAuth.JWT.Decode (error)",
70 | Test_Decode_Error'Access);
71 |
72 | end Add_Tests;
73 |
74 | -- ------------------------------
75 | -- Test Decode operation with errors.
76 | -- ------------------------------
77 | procedure Test_Decode_Error (T : in out Test) is
78 | K : constant String := "eyJhbxGciOiJSUzI1NiIsImtpZCI6IjVmOTBlMWExMGE4YzgwZWJhZWNmYzM4NzBjZDl"
79 | & "lMGVhMGI3ZDVmZGMifQ.eyJpc3xMiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiYXRfaGFzaCI6Im9Ka19EYnFvb1"
80 | & "FVc0FhY3k2cnkxeHciLCJhdWQiOiI4NzI2NTU5OTQwMTQuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJz"
81 | & "dWIiOiIxMDgzNjA3MDMwOTk3MDg5Nzg4NzAiLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJhenAiOiI4NzI2NT"
82 | & "U5OTQwMTQuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJlbWFpbCI6InN0ZXBoYW5lLmNhcnJlekBnbWFp"
83 | & "bC5jb20iLCJpYXQiOjEzNjg5NjgyMzMsImV4cCI6MTM2ODk3MjEzM30.UL1qp2wmleV-ED2A_hlqgDLIGgJB3f"
84 | & "_N7fiz1CgttJcwbmMVwhag3ox2WE9C1KwXhrjwT8eigZ0WkDys5WO1dYs2G1QbDZPnsYYMyHK9XpycaDMEKtVZ"
85 | & "C4C6DkB1SrBHbN0Tv6ExWpszzp1JEL8nZnHd3T_AA3paqONnkvQw_yx";
86 | R : Token;
87 | pragma Unreferenced (R);
88 | begin
89 | R := Decode (K);
90 | T.Fail ("No exception raised");
91 | T.Assert (False, "Bad");
92 |
93 | exception
94 | when Invalid_Token =>
95 | null;
96 | end Test_Decode_Error;
97 |
98 | end Security.OAuth.JWT.Tests;
99 |
--------------------------------------------------------------------------------
/docs/Security_Auth.md:
--------------------------------------------------------------------------------
1 | # Authentication
2 | The `Security.Auth` package implements an authentication framework that is
3 | suitable for OpenID 2.0, OAuth 2.0 and later for OpenID Connect. It allows an application
4 | to authenticate users using an external authorization server such as Google, Facebook,
5 | Google +, Twitter and others.
6 |
7 | See [OpenID Authentication 2.0](https://openid.net/specs/openid-authentication-2_0.html) - Final
8 | https://openid.net/specs/openid-authentication-2_0.html
9 |
10 | See [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)
11 | https://openid.net/specs/openid-connect-core-1_0.html
12 |
13 | See Facebook API: The Login Flow for Web (without JavaScript SDK)
14 | https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/
15 |
16 | Despite their subtle differences, all these authentication frameworks share almost
17 | a common flow. The API provided by `Security.Auth` defines an abstraction suitable
18 | for all these frameworks.
19 |
20 | There are basically two steps that an application must implement:
21 |
22 | * `Discovery`: to resolve and use the OpenID provider and redirect the user to the
23 | provider authentication form.
24 | * `Verify`: to decode the authentication and check its result.
25 |
26 | 
27 |
28 | The authentication process is the following:
29 |
30 | * The application should redirect the user to the authentication URL.
31 | * The OpenID provider authenticate the user and redirects the user to the callback CB.
32 | * The association is decoded from the callback parameter.
33 | * The `Verify` procedure is called with the association to check the result and
34 | obtain the authentication results.
35 |
36 | ## Initialization
37 | The initialization process must be done before each two steps (discovery and verify).
38 | The Authentication manager must be declared and configured.
39 |
40 | ```Ada
41 | Mgr : Security.Auth.Manager;
42 | ```
43 |
44 | For the configuration, the Initialize procedure is called to configure
45 | the Auth realm and set the authentication return callback URL. The return callback
46 | must be a valid URL that is based on the realm. Example:
47 |
48 | ```Ada
49 | Mgr.Initialize (Name => "http://app.site.com/auth",
50 | Return_To => "http://app.site.com/auth/verify",
51 | Realm => "openid");
52 | ```
53 |
54 | After this initialization, the authentication manager can be used in the authentication
55 | process.
56 |
57 | ## OpenID Configuration
58 | The Open ID provider needs the following configuration parameters:
59 |
60 | ```Ada
61 | openid.realm The OpenID realm parameter passed in the authentication URL.
62 | openid.callback_url The OpenID return_to parameter.
63 | ```
64 |
65 | ### Google+
66 | The Google+ authentication is based on OAuth 2.0 and the OpenID Connect Basic Client Profile.
67 |
68 | See https://developers.google.com/accounts/docs/OAuth2Login
69 |
70 | ## Discovery: creating the authentication URL
71 | The first step is to create an authentication URL to which the user must be redirected.
72 | In this step, we have to create an OpenID manager, discover the OpenID provider,
73 | do the association and get an End_Point . The OpenID provider is specified as an
74 | URL, below is an example for Google OpenID:
75 |
76 | ```Ada
77 | Provider : constant String := "https://www.google.com/accounts/o8/id";
78 | OP : Security.Auth.End_Point;
79 | Assoc : constant Security.Auth.Association_Access := new Security.Auth.Association;
80 | ```
81 |
82 | The following steps are performed:
83 |
84 | * The Discover procedure is called to retrieve from the OpenID provider the XRDS
85 | stream and identify the provider. An End_Point is returned in OP .
86 | * The Associate procedure is called to make the association with the End_Point .
87 | The Association record holds session, and authentication.
88 |
89 | ```Ada
90 | Mgr.Discover (Provider, OP); -- Yadis discovery (get the XRDS file).
91 | Mgr.Associate (OP, Assoc.all);-- Associate and get an end-point with a key.
92 | ```
93 |
94 | After this first step, you must manage to save the association in the HTTP session.
95 | Then you must redirect to the authentication URL that is obtained by using:
96 |
97 | ```Ada
98 | Auth_URL : constant String := Mgr.Get_Authentication_URL (OP, Assoc.all);
99 | ```
100 |
101 | ## Verify: acknowledge the authentication in the callback URL
102 | The second step is done when the user has finished the authentication successfully or not.
103 | For this step, the application must get back the association that was saved in the session.
104 | It must also prepare a parameters object that allows the OpenID framework to get the
105 | URI parameters from the return callback.
106 |
107 | ```Ada
108 | Assoc : Association_Access := ...; -- Get the association saved in the session.
109 | Credential : Security.Auth.Authentication;
110 | Params : Auth_Params;
111 | ```
112 |
113 | The auth manager must be initialized and the Verify procedure is called with
114 | the association, parameters and the authentication result. The Get_Status function
115 | must be used to check that the authentication succeeded.
116 |
117 | ```Ada
118 | Mgr.Verify (Assoc.all, Params, Credential);
119 | if Security.Auth.Get_Status (Credential) = Security.Auth.AUTHENTICATED then ... -- Success.
120 | ```
121 |
122 | ## Principal creation
123 | After the user is successfully authenticated, a user principal can be created and saved in
124 | the session. The user principal can then be used to assign permissions to that user and
125 | enforce the application permissions using the security policy manger.
126 |
127 | ```Ada
128 | P : Security.Auth.Principal_Access := Security.Auth.Create_Principal (Credential);
129 | ```
130 |
131 |
--------------------------------------------------------------------------------
/docs/pandoc.css:
--------------------------------------------------------------------------------
1 | /*
2 | * I add this to html files generated with pandoc.
3 | */
4 |
5 | html {
6 | font-size: 100%;
7 | overflow-y: scroll;
8 | -webkit-text-size-adjust: 100%;
9 | -ms-text-size-adjust: 100%;
10 | }
11 |
12 | body {
13 | color: #444;
14 | font-family: Georgia, Palatino, 'Palatino Linotype', Times, 'Times New Roman', serif;
15 | font-size: 12px;
16 | line-height: 1.7;
17 | padding: 1em;
18 | margin: auto;
19 | max-width: 60em;
20 | background: #fefefe;
21 | }
22 |
23 | a {
24 | color: #0645ad;
25 | text-decoration: none;
26 | }
27 |
28 | a:visited {
29 | color: #0b0080;
30 | }
31 |
32 | a:hover {
33 | color: #06e;
34 | }
35 |
36 | a:active {
37 | color: #faa700;
38 | }
39 |
40 | a:focus {
41 | outline: thin dotted;
42 | }
43 |
44 | *::-moz-selection {
45 | background: rgba(255, 255, 0, 0.3);
46 | color: #000;
47 | }
48 |
49 | *::selection {
50 | background: rgba(255, 255, 0, 0.3);
51 | color: #000;
52 | }
53 |
54 | a::-moz-selection {
55 | background: rgba(255, 255, 0, 0.3);
56 | color: #0645ad;
57 | }
58 |
59 | a::selection {
60 | background: rgba(255, 255, 0, 0.3);
61 | color: #0645ad;
62 | }
63 |
64 | p {
65 | margin: 1em 0;
66 | }
67 |
68 | img {
69 | max-width: 100%;
70 | }
71 |
72 | h1, h2, h3, h4, h5, h6 {
73 | color: #111;
74 | line-height: 125%;
75 | margin-top: 2em;
76 | font-weight: normal;
77 | }
78 |
79 | h4, h5, h6 {
80 | font-weight: bold;
81 | }
82 |
83 | h1 {
84 | font-size: 2.5em;
85 | }
86 |
87 | h2 {
88 | font-size: 2em;
89 | }
90 |
91 | h3 {
92 | font-size: 1.5em;
93 | }
94 |
95 | h4 {
96 | font-size: 1.2em;
97 | }
98 |
99 | h5 {
100 | font-size: 1em;
101 | }
102 |
103 | h6 {
104 | font-size: 0.9em;
105 | }
106 |
107 | blockquote {
108 | color: #666666;
109 | margin: 0;
110 | padding-left: 3em;
111 | border-left: 0.5em #EEE solid;
112 | }
113 |
114 | hr {
115 | display: block;
116 | height: 2px;
117 | border: 0;
118 | border-top: 1px solid #aaa;
119 | border-bottom: 1px solid #eee;
120 | margin: 1em 0;
121 | padding: 0;
122 | }
123 |
124 | pre, code, kbd, samp {
125 | color: #000;
126 | font-family: monospace, monospace;
127 | _font-family: 'courier new', monospace;
128 | font-size: 0.98em;
129 | }
130 |
131 | pre {
132 | white-space: pre;
133 | white-space: pre-wrap;
134 | word-wrap: break-word;
135 | }
136 |
137 | b, strong {
138 | font-weight: bold;
139 | }
140 |
141 | dfn {
142 | font-style: italic;
143 | }
144 |
145 | ins {
146 | background: #ff9;
147 | color: #000;
148 | text-decoration: none;
149 | }
150 |
151 | mark {
152 | background: #ff0;
153 | color: #000;
154 | font-style: italic;
155 | font-weight: bold;
156 | }
157 |
158 | sub, sup {
159 | font-size: 75%;
160 | line-height: 0;
161 | position: relative;
162 | vertical-align: baseline;
163 | }
164 |
165 | sup {
166 | top: -0.5em;
167 | }
168 |
169 | sub {
170 | bottom: -0.25em;
171 | }
172 |
173 | ul, ol {
174 | margin: 1em 0;
175 | padding: 0 0 0 2em;
176 | }
177 |
178 | li p:last-child {
179 | margin-bottom: 0;
180 | }
181 |
182 | ul ul, ol ol {
183 | margin: .3em 0;
184 | }
185 |
186 | dl {
187 | margin-bottom: 1em;
188 | }
189 |
190 | dt {
191 | font-weight: bold;
192 | margin-bottom: .8em;
193 | }
194 |
195 | dd {
196 | margin: 0 0 .8em 2em;
197 | }
198 |
199 | dd:last-child {
200 | margin-bottom: 0;
201 | }
202 |
203 | img {
204 | border: 0;
205 | -ms-interpolation-mode: bicubic;
206 | vertical-align: middle;
207 | }
208 |
209 | figure {
210 | display: block;
211 | text-align: center;
212 | margin: 1em 0;
213 | }
214 |
215 | figure img {
216 | border: none;
217 | margin: 0 auto;
218 | }
219 |
220 | figcaption {
221 | font-size: 0.8em;
222 | font-style: italic;
223 | margin: 0 0 .8em;
224 | }
225 |
226 | table {
227 | margin-bottom: 2em;
228 | border-bottom: 1px solid #ddd;
229 | border-right: 1px solid #ddd;
230 | border-spacing: 0;
231 | border-collapse: collapse;
232 | }
233 |
234 | table th {
235 | padding: .2em 1em;
236 | background-color: #eee;
237 | border-top: 1px solid #ddd;
238 | border-left: 1px solid #ddd;
239 | }
240 |
241 | table td {
242 | padding: .2em 1em;
243 | border-top: 1px solid #ddd;
244 | border-left: 1px solid #ddd;
245 | vertical-align: top;
246 | }
247 |
248 | .author {
249 | font-size: 1.2em;
250 | text-align: center;
251 | }
252 |
253 | @media only screen and (min-width: 480px) {
254 | body {
255 | font-size: 14px;
256 | }
257 | }
258 | @media only screen and (min-width: 768px) {
259 | body {
260 | font-size: 16px;
261 | }
262 | }
263 | @media print {
264 | * {
265 | background: transparent !important;
266 | color: black !important;
267 | filter: none !important;
268 | -ms-filter: none !important;
269 | }
270 |
271 | body {
272 | font-size: 12pt;
273 | max-width: 100%;
274 | }
275 |
276 | a, a:visited {
277 | text-decoration: underline;
278 | }
279 |
280 | hr {
281 | height: 1px;
282 | border: 0;
283 | border-bottom: 1px solid black;
284 | }
285 |
286 | a[href]:after {
287 | content: " (" attr(href) ")";
288 | }
289 |
290 | abbr[title]:after {
291 | content: " (" attr(title) ")";
292 | }
293 |
294 | .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after {
295 | content: "";
296 | }
297 |
298 | pre, blockquote {
299 | border: 1px solid #999;
300 | padding-right: 1em;
301 | page-break-inside: avoid;
302 | }
303 |
304 | tr, img {
305 | page-break-inside: avoid;
306 | }
307 |
308 | img {
309 | max-width: 100% !important;
310 | }
311 |
312 | @page :left {
313 | margin: 15mm 20mm 15mm 10mm;
314 | }
315 |
316 | @page :right {
317 | margin: 15mm 10mm 15mm 20mm;
318 | }
319 |
320 | p, h2, h3 {
321 | orphans: 3;
322 | widows: 3;
323 | }
324 |
325 | h2, h3 {
326 | page-break-after: avoid;
327 | }
328 | }
329 |
--------------------------------------------------------------------------------
/docs/Security.md:
--------------------------------------------------------------------------------
1 | # Security
2 | The `Security` package provides a security framework that allows
3 | an application to use OpenID or OAuth security frameworks. This security
4 | framework was first developed within the [Ada Server Faces](https://github.com/stcarrez/ada-asf) project.
5 | It was moved to a separate project so that it can easily be used with [AWS](https://github.com/AdaCore/aws).
6 | This package defines abstractions that are close or similar to Java
7 | security package.
8 |
9 | The security framework uses the following abstractions:
10 |
11 | * **Policy and policy manager**:
12 | The `Policy` defines and implements the set of security rules that specify how to
13 | protect the system or resources. The `Policy_Manager` maintains the security policies.
14 |
15 | * **Principal**:
16 | The `Principal` is the entity that can be authenticated. A principal is obtained
17 | after successful authentication of a user or of a system through an authorization process.
18 | The OpenID or OAuth authentication processes generate such security principal.
19 |
20 | * **Permission**:
21 | The `Permission` represents an access to a system or application resource.
22 | A permission is checked by using the security policy manager. The policy manager uses a
23 | security controller to enforce the permission.
24 |
25 | The `Security_Context` holds the contextual information that the security controller
26 | can use to verify the permission. The security context is associated with a principal and
27 | a set of policy context.
28 |
29 | ## Overview
30 | An application will create a security policy manager and register one or several security
31 | policies (yellow). The framework defines a simple role based security policy and an URL
32 | security policy intended to provide security in web applications. The security policy manager
33 | reads some security policy configuration file which allows the security policies to configure
34 | and create the security controllers. These controllers will enforce the security according
35 | to the application security rules. All these components are built only once when
36 | an application starts.
37 |
38 | A user is authenticated through an authentication system which creates a `Principal`
39 | instance that identifies the user (green). The security framework provides two authentication
40 | systems: OpenID and OAuth 2.0 OpenID Connect.
41 |
42 | 
43 |
44 | When a permission must be enforced, a security context is created and linked to the
45 | `Principal` instance (blue). Additional security policy context can be added depending
46 | on the application context. To check the permission, the security policy manager is called
47 | and it will ask a security controller to verify the permission.
48 |
49 | The framework allows an application to plug its own security policy, its own policy context,
50 | its own principal and authentication mechanism.
51 |
52 | ## Permission
53 | The Security.Permissions package defines the different permissions that can be
54 | checked by the access control manager. An application should declare each permission
55 | by instantiating the Definition package:
56 |
57 | ```Ada
58 | package Perm_Create_Workspace is new Security.Permissions.Definition ("create-workspace");
59 | ```
60 |
61 | This declares a permission that can be represented by "create-workspace " in
62 | configuration files. In Ada, the permission is used as follows:
63 |
64 | ```Ada
65 | Perm_Create_Workspace.Permission
66 | ```
67 |
68 | ## Principal
69 | A principal is created by using either the [Security_Auth OpenID],
70 | the [Security_OAuth OAuth] or another authentication mechanism. The authentication produces
71 | an object that must implement the `Principal` interface. For example:
72 |
73 | ```Ada
74 | P : Security.Auth.Principal_Access := Security.Auth.Create_Principal (Auth);
75 | ```
76 |
77 | or
78 |
79 | ```Ada
80 | P : Security.OAuth.Clients.Access_Token_Access := Security.OAuth.Clients.Create_Access_Token
81 | ```
82 |
83 | The principal is then stored in a security context.
84 |
85 | ## Security Context
86 | The security context provides contextual information for a security controller to
87 | verify that a permission is granted.
88 | This security context is used as follows:
89 |
90 | * An instance of the security context is declared within a function/procedure as
91 | a local variable. This instance will be associated internally with the current thread
92 | through a task attribute.
93 | * The security context is populated with information to identify the current user,
94 | his roles, permissions and other information that could be used by security controllers.
95 | * To verify a permission, the current security context is retrieved and the
96 | Has_Permission operation is called. This operation will use the security manager
97 | to find the security controller associated with the permission to verify.
98 | * The security controller will be called with the security context to check the permission.
99 | The whole job of checking the permission is done by the security controller or its
100 | associated policy manager. The security controller retrieves information from the
101 | security context to decide whether the permission is granted or not.
102 |
103 | For example the security context is declared as follows:
104 |
105 | ```Ada
106 | Context : Security.Contexts.Security_Context;
107 | ```
108 |
109 | A security policy and a principal must be set in the security context. The security policy
110 | defines the rules that govern the security and the principal identifies the current user.
111 |
112 | ```Ada
113 | Context.Set_Context (Policy_Manager, P);
114 | ```
115 |
116 | A permission is checked by using the Has_Permission operation:
117 |
118 | ```Ada
119 | if Security.Contexts.Has_Permission (Perm_Create_Workspace.Permission) then
120 | -- Granted
121 | else
122 | -- Denied
123 | end if;
124 | ```
125 |
126 |
127 |
--------------------------------------------------------------------------------
/docs/Security_OAuth.md:
--------------------------------------------------------------------------------
1 | # OAuth
2 | The Security.OAuth package defines and implements the OAuth 2.0 authorization
3 | framework as defined by the IETF working group in [RFC 6749](https://tools.ietf.org/html/rfc6749):
4 | The OAuth 2.0 Authorization Framework.
5 |
6 | ## OAuth Client
7 | The `Security.OAuth.Clients` package implements the client OAuth 2.0 authorization.
8 |
9 | ### Application setup
10 | For an OAuth2 client application to authenticate, it must be registered on the server
11 | and the server provides the following information:
12 |
13 | * **client_id**: the client identifier is a unique string that identifies the application.
14 | * **client_secret** the client secret is a secret shared between the server and the
15 | client application. The client secret is optional.
16 |
17 | The `Security.OAuth.Clients.Application` tagged record is the primary type that
18 | allows to perform one of the OAuth 2.0 authorization flows. It is necessary to
19 | declare an `Application` instance and register the **client_id**, the **client_secret**
20 | and the authorisation URLs to connect to the server.
21 |
22 | ```Ada
23 | App : Security.OAuth.Clients.Application;
24 | ...
25 | App.Set_Application_Identifier ("app-identifier");
26 | App.Set_Application_Secret ("app-secret");
27 | App.Set_Provider_URL ("https://graph.facebook.com/oauth/access_token");
28 |
29 | ```
30 |
31 | ### Resource Owner Password Credentials Grant
32 | The [RFC 6749](https://tools.ietf.org/html/rfc6749): 4.3. Resource Owner Password Credentials Grant allows to authorize an
33 | application by using the user's name and password. This is the simplest OAuth flow
34 | but because it requires to know the user's name and password, it is not recommended and
35 | not supported by several servers. To use this authorization, the application will use
36 | the `Request_Token` procedure and will give the user's name, password and the scope
37 | of permissions. When the authorization succeeds, a `Grant_Type` token object is returned.
38 |
39 | ```Ada
40 | Token : Security.OAuth.Clients.Grant_Type;
41 | ...
42 | App.Request_Token ("admin", "admin", "scope", Token);
43 | ```
44 |
45 | ### Refreshing an access token
46 | An access token has an expiration date and a new access token must be asked by using the
47 | refresh token. When the access token has expired, the grant token object can be refreshed
48 | to retrieve a new access token by using the `Refresh_Token` procedure. The scope of
49 | permissions can also be passed.
50 |
51 | ```Ada
52 | App.Refresh_Token ("scope", Token);
53 | ```
54 |
55 | ## OAuth Server
56 | OAuth server side is provided by the Security.OAuth.Servers package.
57 | This package allows to implement the authorization framework described in [RFC 6749](https://tools.ietf.org/html/rfc6749)
58 | "The OAuth 2.0 Authorization Framework".
59 |
60 | The authorization method produces a Grant_Type object that contains the result
61 | of the grant (successful or denied). It is the responsibility of the caller to format
62 | the result in JSON/XML and return it to the client.
63 |
64 | Three important operations are defined for the OAuth 2.0 framework. They will be used
65 | in the following order:
66 |
67 | Authorize is used to obtain an authorization request. This operation is
68 | optional in the OAuth 2.0 framework since some authorization method directly return
69 | the access token. This operation is used by the "Authorization Code Grant" and the
70 | "Implicit Grant".
71 |
72 | Token is used to get the access token and optional refresh token. Each time it
73 | is called, a new token is generated.
74 |
75 | Authenticate is used for the API request to verify the access token
76 | and authenticate the API call. This operation can be called several times with the same
77 | token until the token is revoked or it has expired.
78 |
79 | Several grant types are supported.
80 |
81 | ### Application Manager
82 | The application manager maintains the repository of applications which are known by
83 | the server and which can request authorization. Each application is identified by
84 | a client identifier (represented by the client_id request parameter).
85 | The application defines the authorization methods which are allowed as well as
86 | the parameters to control and drive the authorization. This includes the redirection
87 | URI, the application secret, the expiration delay for the access token.
88 |
89 | The application manager is implemented by the application server and it must
90 | implement the Application_Manager interface with the Find_Application
91 | method. The Find_Application is one of the first call made during the
92 | authenticate and token generation phases.
93 |
94 | ### Resource Owner Password Credentials Grant
95 | The password grant is one of the easiest grant method to understand but it is also one
96 | of the less secure. In this grant method, the username and user password are passed in
97 | the request parameter together with the application client identifier. The realm verifies
98 | the username and password and when they are correct it generates the access token with
99 | an optional refresh token. The realm also returns in the grant the user principal that
100 | identifies the user.
101 |
102 | ```Ada
103 | Realm : Security.OAuth.Servers.Auth_Manager;
104 | Grant : Security.OAuth.Servers.Grant_Type;
105 | Realm.Token (Params, Grant);
106 |
107 | ```
108 |
109 | ### Accessing Protected Resources
110 | When accessing a protected resource, the API implementation will use the
111 | Authenticate operation to verify the access token and get a security principal.
112 | The security principal will identify the resource owner as well as the application
113 | that is doing the call.
114 |
115 | ```Ada
116 | Realm : Security.OAuth.Servers.Auth_Manager;
117 | Grant : Security.OAuth.Servers.Grant_Type;
118 | Token : String := ...;
119 | Realm.Authenticate (Token, Grant);
120 |
121 | ```
122 |
123 | When a security principal is returned, the access token was validated and the
124 | request is granted for the application.
125 |
126 |
127 |
--------------------------------------------------------------------------------
/src/security-auth-oauth.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-auth-oauth -- OAuth based authentication
3 | -- Copyright (C) 2013, 2017, 2020 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Util.Log.Loggers;
9 | package body Security.Auth.OAuth is
10 |
11 | Log : constant Util.Log.Loggers.Logger := Util.Log.Loggers.Create ("Security.Auth.OAuth");
12 |
13 | -- ------------------------------
14 | -- OAuth Manager
15 | -- ------------------------------
16 | -- The Manager provides the core operations for the OAuth authorization process.
17 |
18 | -- ------------------------------
19 | -- Initialize the authentication realm.
20 | -- ------------------------------
21 | overriding
22 | procedure Initialize (Realm : in out Manager;
23 | Params : in Parameters'Class;
24 | Provider : in String := PROVIDER_OPENID) is
25 | begin
26 | Realm.App.Set_Application_Identifier (Params.Get_Parameter (Provider & ".client_id"));
27 | Realm.App.Set_Application_Secret (Params.Get_Parameter (Provider & ".secret"));
28 | Realm.App.Set_Application_Callback (Params.Get_Parameter (Provider & ".callback_url"));
29 | Realm.App.Set_Provider_URI (Params.Get_Parameter (Provider & ".request_url"));
30 | Realm.Realm := To_Unbounded_String (Params.Get_Parameter (Provider & ".realm"));
31 | Realm.Scope := To_Unbounded_String (Params.Get_Parameter (Provider & ".scope"));
32 | Realm.Issuer := To_Unbounded_String (Params.Get_Parameter (Provider & ".issuer"));
33 | end Initialize;
34 |
35 | -- ------------------------------
36 | -- Discover the OpenID provider that must be used to authenticate the user.
37 | -- The Name can be an URL or an alias that identifies the provider.
38 | -- A cached OpenID provider can be returned.
39 | -- Read the XRDS document from the URI and initialize the OpenID provider end point.
40 | -- (See OpenID Section 7.3 Discovery)
41 | -- ------------------------------
42 | overriding
43 | procedure Discover (Realm : in out Manager;
44 | Name : in String;
45 | Result : out End_Point) is
46 | pragma Unreferenced (Realm);
47 | begin
48 | Result.URL := To_Unbounded_String (Name);
49 | end Discover;
50 |
51 | -- ------------------------------
52 | -- Associate the application (relying party) with the OpenID provider.
53 | -- The association can be cached.
54 | -- (See OpenID Section 8 Establishing Associations)
55 | -- ------------------------------
56 | overriding
57 | procedure Associate (Realm : in out Manager;
58 | OP : in End_Point;
59 | Result : out Association) is
60 | pragma Unreferenced (Realm, OP);
61 | begin
62 | Result.Assoc_Handle := To_Unbounded_String (Security.OAuth.Clients.Create_Nonce (128));
63 | Result.Nonce := To_Unbounded_String (Security.OAuth.Clients.Create_Nonce (256));
64 | end Associate;
65 |
66 | -- ------------------------------
67 | -- Get the authentication URL to which the user must be redirected for authentication
68 | -- by the authentication server.
69 | -- ------------------------------
70 | overriding
71 | function Get_Authentication_URL (Realm : in Manager;
72 | OP : in End_Point;
73 | Assoc : in Association) return String is
74 | Result : Unbounded_String := OP.URL;
75 | State : constant String := Realm.App.Get_State (To_String (Assoc.Assoc_Handle));
76 | Params : constant String := Realm.App.Get_Auth_Params (State, To_String (Realm.Scope));
77 | begin
78 | if Index (Result, "?") > 0 then
79 | Append (Result, "&");
80 | else
81 | Append (Result, "?");
82 | end if;
83 | Append (Result, Params);
84 | Append (Result, "&");
85 | Append (Result, Security.OAuth.RESPONSE_TYPE);
86 | Append (Result, "=code");
87 | Append (Result, "&");
88 | Append (Result, Security.OAuth.NONCE_TOKEN);
89 | Append (Result, "=");
90 | Append (Result, Assoc.Nonce);
91 |
92 | Log.Debug ("Params = {0}", Params);
93 | return To_String (Result);
94 | end Get_Authentication_URL;
95 |
96 | -- ------------------------------
97 | -- Verify the authentication result
98 | -- ------------------------------
99 | overriding
100 | procedure Verify (Realm : in out Manager;
101 | Assoc : in Association;
102 | Request : in Parameters'Class;
103 | Result : out Authentication) is
104 | State : constant String := Request.Get_Parameter (Security.OAuth.STATE);
105 | Code : constant String := Request.Get_Parameter (Security.OAuth.CODE);
106 | Error : constant String := Request.Get_Parameter (Security.OAuth.ERROR_DESCRIPTION);
107 | begin
108 | if Error'Length /= 0 then
109 | Set_Result (Result, CANCEL, "Authentication refused: " & Error);
110 | return;
111 | end if;
112 |
113 | -- First, verify that the state parameter matches our internal state.
114 | if not Realm.App.Is_Valid_State (To_String (Assoc.Assoc_Handle), State) then
115 | Set_Result (Result, INVALID_SIGNATURE, "invalid OAuth state parameter");
116 | return;
117 | end if;
118 |
119 | -- Get the access token from the authorization code.
120 | declare
121 | use type Security.OAuth.Clients.Access_Token_Access;
122 |
123 | Acc : constant Security.OAuth.Clients.Access_Token_Access
124 | := Realm.App.Request_Access_Token (Code);
125 | begin
126 | if Acc = null then
127 | Set_Result (Result, INVALID_SIGNATURE, "cannot change the code to an access_token");
128 | return;
129 | end if;
130 |
131 | -- Last step, verify the access token and get the user identity.
132 | Manager'Class (Realm).Verify_Access_Token (Assoc, Request, Acc, Result);
133 | end;
134 |
135 | end Verify;
136 |
137 | end Security.Auth.OAuth;
138 |
--------------------------------------------------------------------------------
/docs/Security_Policies.md:
--------------------------------------------------------------------------------
1 | # Security Policies
2 | The Security Policy defines and implements the set of security rules that specify
3 | how to protect the system or resources. The `Policy_Manager` maintains
4 | the security policies. These policies are registered when an application starts,
5 | before reading the policy configuration files.
6 |
7 | 
8 |
9 | While the policy configuration files are processed, the policy instances that have been
10 | registered will create a security controller and bind it to a given permission. After
11 | successful initialization, the `Policy_Manager` contains a list of securiy
12 | controllers which are associated with each permission defined by the application.
13 |
14 | ## Authenticated Permission
15 | The `auth-permission` is a pre-defined permission that can be configured in the XML
16 | configuration. Basically the permission is granted if the security context has a principal.
17 | Otherwise the permission is denied. The permission is assigned a name and is declared
18 | as follows:
19 |
20 | ```Ada
21 |
22 |
23 | view-profile
24 |
25 |
26 | ```
27 |
28 | This example defines the `view-profile` permission.
29 |
30 | ## Grant Permission
31 | The `grant-permission` is another pre-defined permission that gives the permission whatever
32 | the security context. The permission is defined as follows:
33 |
34 | ```Ada
35 |
36 |
37 | anonymous
38 |
39 |
40 | ```
41 |
42 | This example defines the `anonymous` permission.
43 |
44 | ## Role Based Security Policy
45 | The `Security.Policies.Roles` package implements a role based security policy.
46 | In this policy, users are assigned one or several roles and permissions are
47 | associated with roles. A permission is granted if the user has one of the roles required
48 | by the permission.
49 |
50 | ### Policy creation
51 | An instance of the `Role_Policy` must be created and registered in the policy manager.
52 | Get or declare the following variables:
53 |
54 | ```Ada
55 | Manager : Security.Policies.Policy_Manager;
56 | Policy : Security.Policies.Roles.Role_Policy_Access;
57 | ```
58 |
59 | Create the role policy and register it in the policy manager as follows:
60 |
61 | ```Ada
62 | Policy := new Role_Policy;
63 | Manager.Add_Policy (Policy.all'Access);
64 | ```
65 |
66 | ### Policy Configuration
67 | A role is represented by a name in security configuration files. A role based permission
68 | is associated with a list of roles. The permission is granted if the user has one of these
69 | roles. When the role based policy is registered in the policy manager, the following
70 | XML configuration is used:
71 |
72 | ```Ada
73 |
74 |
75 | admin
76 |
77 |
78 | manager
79 |
80 |
81 | create-workspace
82 | admin
83 | manager
84 |
85 | ...
86 |
87 | ```
88 |
89 | This definition declares two roles: `admin` and `manager`
90 | It defines a permission `create-workspace` that will be granted if the
91 | user has either the `admin` or the `manager` role.
92 |
93 | Each role is identified by a name in the configuration file. It is represented by
94 | a `Role_Type`. To provide an efficient implementation, the `Role_Type`
95 | is represented as an integer with a limit of 64 different roles.
96 |
97 | ### Assigning roles to users
98 | A `Security_Context` must be associated with a set of roles before checking the
99 | permission. This is done by using the `Set_Role_Context` operation:
100 |
101 | ```Ada
102 | Security.Policies.Roles.Set_Role_Context (Security.Contexts.Current, "admin");
103 | ```
104 |
105 | ## URL Security Policy
106 | The `Security.Policies.Urls` implements a security policy intended to be used
107 | in web servers. It allows to protect an URL by defining permissions that must be granted
108 | for a user to get access to the URL. A typical example is a web server that has a set of
109 | administration pages, these pages should be accessed by users having some admin permission.
110 |
111 | ### Policy creation
112 | An instance of the `URL_Policy` must be created and registered in the policy manager.
113 | Get or declare the following variables:
114 |
115 | ```Ada
116 | Manager : Security.Policies.Policy_Manager;
117 | Policy : Security.Policies.Urls.URL_Policy_Access;
118 | ```
119 |
120 | Create the URL policy and register it in the policy manager as follows:
121 |
122 | ```Ada
123 | Policy := new URL_Policy;
124 | Manager.Add_Policy (Policy.all'Access);
125 | ```
126 |
127 | ### Policy Configuration
128 | Once the URL policy is registered, the policy manager can read and process the following
129 | XML configuration:
130 |
131 | ```Ada
132 |
133 |
134 | create-workspace
135 | admin
136 | /workspace/create
137 | /workspace/setup/*
138 |
139 | ...
140 |
141 | ```
142 |
143 | This policy gives access to the URL that match one of the URL pattern if the
144 | security context has the permission `create-workspace` or `admin`.
145 | These two permissions are checked according to another security policy.
146 | The XML configuration can define several `url-policy`. When the `order`
147 | attribute is not defined, they are checked in the order defined in
148 | the XML. In other words, the first `url-policy` that matches the URL is used
149 | to verify the permission. When the `order` attribute is set to `first`,
150 | the rule is added at begining of the list. This special case is intended to
151 | allow overriding a previous definition in some configuration file.
152 |
153 | The `url-policy` definition can contain several `permission`.
154 | The first permission that is granted gives access to the URL.
155 |
156 | ### Checking for permission
157 | To check a URL permission, you must declare a `URL_Permission` object with the URL.
158 |
159 | ```Ada
160 | URL : constant String := ...;
161 | Perm : constant Policies.URLs.URL_Permission (URL'Length)
162 | := URL_Permission '(Len => URI'Length, URL => URL);
163 | ```
164 |
165 | Then, we can check the permission:
166 |
167 | ```Ada
168 | Result : Boolean := Security.Contexts.Has_Permission (Perm);
169 | ```
170 |
171 |
--------------------------------------------------------------------------------
/samples/web/atlas/css/grids/fluid_grid.css:
--------------------------------------------------------------------------------
1 | /*
2 | Variable Grid System (Fluid Version).
3 | Learn more ~ http://www.spry-soft.com/grids/
4 | Based on 960 Grid System - http://960.gs/ & 960 Fluid - http://www.designinfluences.com/
5 |
6 | Licensed under GPL and MIT.
7 | */
8 |
9 |
10 | /* Containers
11 | ----------------------------------------------------------------------------------------------------*/
12 | .container_12 {
13 | width: 92%;
14 | margin-left: 4%;
15 | margin-right: 4%;
16 | }
17 |
18 | /* Grid >> Global
19 | ----------------------------------------------------------------------------------------------------*/
20 |
21 | .grid_1,
22 | .grid_2,
23 | .grid_3,
24 | .grid_4,
25 | .grid_5,
26 | .grid_6,
27 | .grid_7,
28 | .grid_8,
29 | .grid_9,
30 | .grid_10,
31 | .grid_11,
32 | .grid_12 {
33 | display:inline;
34 | float: left;
35 | position: relative;
36 | margin-left: 1%;
37 | margin-right: 1%;
38 | }
39 |
40 | /* Grid >> Children (Alpha ~ First, Omega ~ Last)
41 | ----------------------------------------------------------------------------------------------------*/
42 |
43 | .alpha {
44 | margin-left: 0;
45 | }
46 |
47 | .omega {
48 | margin-right: 0;
49 | }
50 |
51 | /* Grid >> 12 Columns
52 | ----------------------------------------------------------------------------------------------------*/
53 |
54 |
55 | .container_12 .grid_1 {
56 | width:6.333%;
57 | }
58 |
59 | .container_12 .grid_2 {
60 | width:14.667%;
61 | }
62 |
63 | .container_12 .grid_3 {
64 | width:23.0%;
65 | }
66 |
67 | .container_12 .grid_4 {
68 | width:31.333%;
69 | }
70 |
71 | .container_12 .grid_5 {
72 | width:39.667%;
73 | }
74 |
75 | .container_12 .grid_6 {
76 | width:48.0%;
77 | }
78 |
79 | .container_12 .grid_7 {
80 | width:56.333%;
81 | }
82 |
83 | .container_12 .grid_8 {
84 | width:64.667%;
85 | }
86 |
87 | .container_12 .grid_9 {
88 | width:73.0%;
89 | }
90 |
91 | .container_12 .grid_10 {
92 | width:81.333%;
93 | }
94 |
95 | .container_12 .grid_11 {
96 | width:89.667%;
97 | }
98 |
99 | .container_12 .grid_12 {
100 | width:98.0%;
101 | }
102 |
103 |
104 |
105 | /* Prefix Extra Space >> 12 Columns
106 | ----------------------------------------------------------------------------------------------------*/
107 |
108 |
109 | .container_12 .prefix_1 {
110 | padding-left:8.333%;
111 | }
112 |
113 | .container_12 .prefix_2 {
114 | padding-left:16.667%;
115 | }
116 |
117 | .container_12 .prefix_3 {
118 | padding-left:25.0%;
119 | }
120 |
121 | .container_12 .prefix_4 {
122 | padding-left:33.333%;
123 | }
124 |
125 | .container_12 .prefix_5 {
126 | padding-left:41.667%;
127 | }
128 |
129 | .container_12 .prefix_6 {
130 | padding-left:50.0%;
131 | }
132 |
133 | .container_12 .prefix_7 {
134 | padding-left:58.333%;
135 | }
136 |
137 | .container_12 .prefix_8 {
138 | padding-left:66.667%;
139 | }
140 |
141 | .container_12 .prefix_9 {
142 | padding-left:75.0%;
143 | }
144 |
145 | .container_12 .prefix_10 {
146 | padding-left:83.333%;
147 | }
148 |
149 | .container_12 .prefix_11 {
150 | padding-left:91.667%;
151 | }
152 |
153 |
154 |
155 | /* Suffix Extra Space >> 12 Columns
156 | ----------------------------------------------------------------------------------------------------*/
157 |
158 |
159 | .container_12 .suffix_1 {
160 | padding-right:8.333%;
161 | }
162 |
163 | .container_12 .suffix_2 {
164 | padding-right:16.667%;
165 | }
166 |
167 | .container_12 .suffix_3 {
168 | padding-right:25.0%;
169 | }
170 |
171 | .container_12 .suffix_4 {
172 | padding-right:33.333%;
173 | }
174 |
175 | .container_12 .suffix_5 {
176 | padding-right:41.667%;
177 | }
178 |
179 | .container_12 .suffix_6 {
180 | padding-right:50.0%;
181 | }
182 |
183 | .container_12 .suffix_7 {
184 | padding-right:58.333%;
185 | }
186 |
187 | .container_12 .suffix_8 {
188 | padding-right:66.667%;
189 | }
190 |
191 | .container_12 .suffix_9 {
192 | padding-right:75.0%;
193 | }
194 |
195 | .container_12 .suffix_10 {
196 | padding-right:83.333%;
197 | }
198 |
199 | .container_12 .suffix_11 {
200 | padding-right:91.667%;
201 | }
202 |
203 |
204 |
205 | /* Push Space >> 12 Columns
206 | ----------------------------------------------------------------------------------------------------*/
207 |
208 |
209 | .container_12 .push_1 {
210 | left:8.333%;
211 | }
212 |
213 | .container_12 .push_2 {
214 | left:16.667%;
215 | }
216 |
217 | .container_12 .push_3 {
218 | left:25.0%;
219 | }
220 |
221 | .container_12 .push_4 {
222 | left:33.333%;
223 | }
224 |
225 | .container_12 .push_5 {
226 | left:41.667%;
227 | }
228 |
229 | .container_12 .push_6 {
230 | left:50.0%;
231 | }
232 |
233 | .container_12 .push_7 {
234 | left:58.333%;
235 | }
236 |
237 | .container_12 .push_8 {
238 | left:66.667%;
239 | }
240 |
241 | .container_12 .push_9 {
242 | left:75.0%;
243 | }
244 |
245 | .container_12 .push_10 {
246 | left:83.333%;
247 | }
248 |
249 | .container_12 .push_11 {
250 | left:91.667%;
251 | }
252 |
253 |
254 |
255 | /* Pull Space >> 12 Columns
256 | ----------------------------------------------------------------------------------------------------*/
257 |
258 |
259 | .container_12 .pull_1 {
260 | left:-8.333%;
261 | }
262 |
263 | .container_12 .pull_2 {
264 | left:-16.667%;
265 | }
266 |
267 | .container_12 .pull_3 {
268 | left:-25.0%;
269 | }
270 |
271 | .container_12 .pull_4 {
272 | left:-33.333%;
273 | }
274 |
275 | .container_12 .pull_5 {
276 | left:-41.667%;
277 | }
278 |
279 | .container_12 .pull_6 {
280 | left:-50.0%;
281 | }
282 |
283 | .container_12 .pull_7 {
284 | left:-58.333%;
285 | }
286 |
287 | .container_12 .pull_8 {
288 | left:-66.667%;
289 | }
290 |
291 | .container_12 .pull_9 {
292 | left:-75.0%;
293 | }
294 |
295 | .container_12 .pull_10 {
296 | left:-83.333%;
297 | }
298 |
299 | .container_12 .pull_11 {
300 | left:-91.667%;
301 | }
302 |
303 |
304 |
305 |
306 | /* Clear Floated Elements
307 | ----------------------------------------------------------------------------------------------------*/
308 |
309 | /* http://sonspring.com/journal/clearing-floats */
310 |
311 | .clear {
312 | clear: both;
313 | display: block;
314 | overflow: hidden;
315 | visibility: hidden;
316 | width: 0;
317 | height: 0;
318 | }
319 |
320 | /* http://perishablepress.com/press/2008/02/05/lessons-learned-concerning-the-clearfix-css-hack */
321 |
322 | .clearfix:after {
323 | clear: both;
324 | content: ' ';
325 | display: block;
326 | font-size: 0;
327 | line-height: 0;
328 | visibility: hidden;
329 | width: 0;
330 | height: 0;
331 | }
332 |
333 | .clearfix {
334 | display: inline-block;
335 | }
336 |
337 | * html .clearfix {
338 | height: 1%;
339 | }
340 |
341 | .clearfix {
342 | display: block;
343 | }
--------------------------------------------------------------------------------
/src/security-random.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-random -- Random numbers for nonce, secret keys, token generation
3 | -- Copyright (C) 2017, 2022, 2023, 2025 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 | with Ada.Calendar;
8 | with Ada.Calendar.Conversions;
9 | with Util.Encoders.Base64;
10 |
11 | package body Security.Random is
12 |
13 | use Interfaces;
14 |
15 | -- ------------------------------
16 | -- Initialize the random generator.
17 | -- ------------------------------
18 | overriding
19 | procedure Initialize (Gen : in out Generator) is
20 | begin
21 | Gen.Rand.Reset;
22 | end Initialize;
23 |
24 | -- ------------------------------
25 | -- Fill the array with pseudo-random numbers.
26 | -- ------------------------------
27 | procedure Generate (Gen : in out Generator;
28 | Into : out Ada.Streams.Stream_Element_Array) is
29 | begin
30 | Gen.Rand.Generate (Into);
31 | end Generate;
32 |
33 | procedure Generate (Gen : in out Generator;
34 | Into : out String) is
35 | Buf : Ada.Streams.Stream_Element_Array (1 .. Into'Length);
36 | for Buf'Address use Into'Address;
37 | begin
38 | Gen.Rand.Generate (Buf);
39 | end Generate;
40 |
41 | -- ------------------------------
42 | -- Generate a random sequence of bits and convert the result
43 | -- into a string in base64url.
44 | -- ------------------------------
45 | function Generate (Gen : in out Generator'Class;
46 | Bits : in Positive) return String is
47 | use type Ada.Streams.Stream_Element_Offset;
48 |
49 | Rand_Count : constant Ada.Streams.Stream_Element_Offset
50 | := Ada.Streams.Stream_Element_Offset (4 * ((Bits + 31) / 32));
51 |
52 | Rand : Ada.Streams.Stream_Element_Array (0 .. Rand_Count - 1);
53 | Buffer : Ada.Streams.Stream_Element_Array (0 .. Rand_Count * 3);
54 | Encoder : Util.Encoders.Base64.Encoder;
55 | Last : Ada.Streams.Stream_Element_Offset;
56 | Encoded : Ada.Streams.Stream_Element_Offset;
57 | begin
58 | -- Generate the random sequence.
59 | Gen.Generate (Rand);
60 |
61 | -- Encode the random stream in base64url and save it into the result string.
62 | Encoder.Set_URL_Mode (True);
63 | Encoder.Transform (Data => Rand, Into => Buffer,
64 | Last => Last, Encoded => Encoded);
65 | Encoder.Finish (Into => Buffer (Last + 1 .. Buffer'Last),
66 | Last => Last);
67 | while Character'Val (Buffer (Last)) = '=' loop
68 | Last := Last - 1;
69 | end loop;
70 | declare
71 | Result : String (1 .. Natural (Last + 1));
72 | begin
73 | for I in 0 .. Last loop
74 | Result (Natural (I + 1)) := Character'Val (Buffer (I));
75 | end loop;
76 | return Result;
77 | end;
78 | end Generate;
79 |
80 | -- ------------------------------
81 | -- Generate a random sequence of bits, convert the result
82 | -- into a string in base64url and append it to the buffer.
83 | -- ------------------------------
84 | procedure Generate (Gen : in out Generator'Class;
85 | Bits : in Positive;
86 | Into : in out Ada.Strings.Unbounded.Unbounded_String) is
87 | use type Ada.Streams.Stream_Element_Offset;
88 |
89 | Rand_Count : constant Ada.Streams.Stream_Element_Offset
90 | := Ada.Streams.Stream_Element_Offset (4 * ((Bits + 31) / 32));
91 |
92 | Rand : Ada.Streams.Stream_Element_Array (0 .. Rand_Count - 1);
93 | Buffer : Ada.Streams.Stream_Element_Array (0 .. Rand_Count * 3);
94 | Encoder : Util.Encoders.Base64.Encoder;
95 | Last : Ada.Streams.Stream_Element_Offset;
96 | Encoded : Ada.Streams.Stream_Element_Offset;
97 | begin
98 | -- Generate the random sequence.
99 | Gen.Generate (Rand);
100 |
101 | -- Encode the random stream in base64url and save it into the result string.
102 | Encoder.Set_URL_Mode (True);
103 | Encoder.Transform (Data => Rand, Into => Buffer,
104 | Last => Last, Encoded => Encoded);
105 | Encoder.Finish (Into => Buffer (Last + 1 .. Buffer'Last),
106 | Last => Last);
107 | while Character'Val (Buffer (Last)) = '=' loop
108 | Last := Last - 1;
109 | end loop;
110 | for I in 0 .. Last loop
111 | Ada.Strings.Unbounded.Append (Into, Character'Val (Buffer (I)));
112 | end loop;
113 | end Generate;
114 |
115 | -- Protected type to allow using the random generator by several tasks.
116 | protected body Raw_Generator is
117 |
118 | procedure Generate (Into : out Ada.Streams.Stream_Element_Array) is
119 | use Ada.Streams;
120 |
121 | Size : constant Ada.Streams.Stream_Element_Offset := Into'Length / 4;
122 | Remain : constant Ada.Streams.Stream_Element_Offset := Into'Length mod 4;
123 | Value : Unsigned_32;
124 | begin
125 | -- Generate the random sequence (fill 32-bits at a time for each random call).
126 | for I in 0 .. Size - 1 loop
127 | Value := Id_Random.Random (Rand);
128 | Into (Into'First + 4 * I) := Stream_Element (Value and 16#0FF#);
129 | Into (Into'First + 4 * I + 1) := Stream_Element (Shift_Right (Value, 8) and 16#0FF#);
130 | Into (Into'First + 4 * I + 2) := Stream_Element (Shift_Right (Value, 16) and 16#0FF#);
131 | Into (Into'First + 4 * I + 3) := Stream_Element (Shift_Right (Value, 24) and 16#0FF#);
132 | end loop;
133 |
134 | -- Fill the remaining bytes.
135 | if Remain > 0 then
136 | Value := Id_Random.Random (Rand);
137 | for I in 0 .. Remain - 1 loop
138 | Into (Into'Last - I) := Stream_Element (Value and 16#0FF#);
139 | Value := Shift_Right (Value, 8);
140 | end loop;
141 | end if;
142 | end Generate;
143 |
144 | procedure Reset is
145 | Now : constant Ada.Calendar.Time := Ada.Calendar.Clock;
146 | Nsec : constant Unsigned_64
147 | := Unsigned_64 (Ada.Calendar.Conversions.To_Unix_Nano_Time (Now));
148 | Low : constant Unsigned_32 := Unsigned_32 (Nsec and 16#0ffff_ffff#);
149 | High : constant Unsigned_32 := Unsigned_32 (Shift_Right (Nsec, 32));
150 | begin
151 | Id_Random.Reset (Rand, Integer ((Low xor High) and 16#07fff_ffff#));
152 | end Reset;
153 |
154 | end Raw_Generator;
155 |
156 | end Security.Random;
157 |
--------------------------------------------------------------------------------
/regtests/src/security-oauth-clients-tests.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- Security-oauth-clients-tests - Unit tests for OAuth
3 | -- Copyright (C) 2013 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 | with Ada.Strings.Fixed;
8 | with Util.Test_Caller;
9 | with Util.Measures;
10 | with Util.Strings.Sets;
11 |
12 | package body Security.OAuth.Clients.Tests is
13 |
14 | package Caller is new Util.Test_Caller (Test, "Security.OAuth.Clients");
15 |
16 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite) is
17 | begin
18 | Caller.Add_Test (Suite, "Test Security.OAuth.Clients.Create_Nonce",
19 | Test_Create_Nonce'Access);
20 | Caller.Add_Test (Suite, "Test Security.OAuth.Clients.Get_State",
21 | Test_Get_State'Access);
22 | Caller.Add_Test (Suite, "Test Security.OAuth.Clients.Is_Valid_State",
23 | Test_Is_Valid_State'Access);
24 | Caller.Add_Test (Suite, "Test Security.OAuth.Clients.Get_Auth_Params",
25 | Test_Get_Auth_Params'Access);
26 | end Add_Tests;
27 |
28 | -- ------------------------------
29 | -- Test Create_Nonce operation.
30 | -- ------------------------------
31 | procedure Test_Create_Nonce (T : in out Test) is
32 | Nonces : Util.Strings.Sets.Set;
33 | begin
34 | for I in 1 .. 1_000 loop
35 | for I in 32 .. 734 loop
36 | declare
37 | S : constant String := Create_Nonce (I * 3);
38 | begin
39 | T.Assert (not Nonces.Contains (S), "Nonce was not unique: " & S);
40 | Nonces.Include (S);
41 | end;
42 | end loop;
43 | end loop;
44 | declare
45 | S : Util.Measures.Stamp;
46 | begin
47 | for I in 1 .. 1_000 loop
48 | declare
49 | Nonce : constant String := Create_Nonce (128);
50 | pragma Unreferenced (Nonce);
51 | begin
52 | null;
53 | end;
54 | end loop;
55 | Util.Measures.Report (S, "128 bits nonce generation", 1_000);
56 | end;
57 | end Test_Create_Nonce;
58 |
59 | -- ------------------------------
60 | -- Test the Get_State operation.
61 | -- ------------------------------
62 | procedure Test_Get_State (T : in out Test) is
63 | App : Application;
64 | Nonce : constant String := Create_Nonce (128);
65 | begin
66 | App.Set_Application_Identifier ("test");
67 | Util.Tests.Assert_Equals (T, "test", App.Get_Application_Identifier, "Invalid application");
68 |
69 | App.Set_Application_Secret ("my-secret");
70 | App.Set_Application_Callback ("my-callback");
71 | App.Set_Provider_URI ("http://my-provider");
72 |
73 | declare
74 | State : constant String := App.Get_State (Nonce);
75 | begin
76 | T.Assert (State'Length > 25, "State is too small: " & State);
77 | T.Assert (Ada.Strings.Fixed.Index (State, Nonce) = 0,
78 | "The state must not contain the nonce");
79 |
80 | -- Calling Get_State with the same nonce should produce the same result.
81 | Util.Tests.Assert_Equals (T, State, App.Get_State (Nonce), "Invalid state");
82 |
83 | App.Set_Application_Secret ("second-secret");
84 | declare
85 | State2 : constant String := App.Get_State (Nonce);
86 | begin
87 | T.Assert (State /= State2,
88 | "Changing the application key should produce a different state");
89 | end;
90 |
91 | -- Restore the secret and change the callback.
92 | App.Set_Application_Secret ("my-secret");
93 | App.Set_Application_Callback ("my-callback2");
94 | declare
95 | State2 : constant String := App.Get_State (Nonce);
96 | begin
97 | T.Assert (State /= State2,
98 | "Changing the application callback should produce a different state");
99 | end;
100 |
101 | -- Restore the callback and change the client Id.
102 | App.Set_Application_Callback ("my-callback");
103 | App.Set_Application_Identifier ("test2");
104 | declare
105 | State2 : constant String := App.Get_State (Nonce);
106 | begin
107 | T.Assert (State /= State2,
108 | "Changing the application identifier should produce a different state");
109 | end;
110 | end;
111 | end Test_Get_State;
112 |
113 | -- ------------------------------
114 | -- Test the Is_Valid_State operation.
115 | -- ------------------------------
116 | procedure Test_Is_Valid_State (T : in out Test) is
117 | App : Application;
118 | begin
119 | App.Set_Application_Identifier ("test");
120 | Util.Tests.Assert_Equals (T, "test", App.Get_Application_Identifier, "Invalid application");
121 |
122 | App.Set_Application_Secret ("my-secret");
123 | App.Set_Application_Callback ("my-callback");
124 | App.Set_Provider_URI ("http://my-provider");
125 |
126 | for I in 1 .. 100 loop
127 | declare
128 | Nonce : constant String := Create_Nonce (128);
129 | State : constant String := App.Get_State (Nonce);
130 | begin
131 | T.Assert (State'Length > 25, "State is too small: " & State);
132 | T.Assert (App.Is_Valid_State (Nonce, State), "Invalid state: " & State);
133 | T.Assert (not App.Is_Valid_State ("", State), "State was valid with invalid nonce");
134 | T.Assert (not App.Is_Valid_State (State, State), "State must be invalid");
135 | T.Assert (not App.Is_Valid_State (Nonce, State & "d"), "State must be invalid");
136 | end;
137 | end loop;
138 | end Test_Is_Valid_State;
139 |
140 | -- Test the Get_Auth_Params operation.
141 | procedure Test_Get_Auth_Params (T : in out Test) is
142 | App : Application;
143 | begin
144 | App.Set_Application_Identifier ("test");
145 | Util.Tests.Assert_Equals (T, "test", App.Get_Application_Identifier, "Invalid application");
146 |
147 | App.Set_Application_Secret ("my-secret");
148 | App.Set_Application_Callback ("my-callback");
149 | App.Set_Provider_URI ("http://my-provider");
150 | declare
151 | P : constant String := App.Get_Auth_Params ("the-state", "the-scope");
152 | begin
153 | Util.Tests.Assert_Equals (T, "client_id=test&redirect_uri=my-callback&"
154 | & "scope=the-scope&state=the-state", P,
155 | "Invalid auth params");
156 | end;
157 | end Test_Get_Auth_Params;
158 |
159 | end Security.OAuth.Clients.Tests;
160 |
--------------------------------------------------------------------------------
/regtests/src/security-auth-tests.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-auth-tests - Tests for OpenID
3 | -- Copyright (C) 2009, 2010, 2011, 2012, 2013, 2015, 2019 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Ada.Strings.Unbounded;
9 |
10 | with Util.Http.Clients.Mockups;
11 |
12 | with Util.Test_Caller;
13 | with Ada.Text_IO;
14 | package body Security.Auth.Tests is
15 |
16 | package Caller is new Util.Test_Caller (Test, "Security.Openid");
17 | procedure Setup (M : in out Manager;
18 | Name : in String);
19 |
20 | procedure Check_Discovery (T : in out Test;
21 | Name : in String;
22 | URI : in String);
23 |
24 | procedure Add_Tests (Suite : in Util.Tests.Access_Test_Suite) is
25 | begin
26 | Caller.Add_Test (Suite, "Test Security.OpenID.Discover",
27 | Test_Discovery'Access);
28 | Caller.Add_Test (Suite, "Test Security.OpenID.Verify_Signature",
29 | Test_Verify_Signature'Access);
30 | end Add_Tests;
31 |
32 | overriding
33 | function Get_Parameter (Params : in Test_Parameters;
34 | Name : in String) return String is
35 | begin
36 | if Params.Params.Contains (Name) then
37 | return Params.Params.Element (Name);
38 | else
39 | return "";
40 | end if;
41 | end Get_Parameter;
42 |
43 | procedure Set_Parameter (Params : in out Test_Parameters;
44 | Name : in String;
45 | Value : in String) is
46 | begin
47 | Params.Params.Include (Name, Value);
48 | end Set_Parameter;
49 |
50 | procedure Setup (M : in out Manager;
51 | Name : in String) is
52 | Params : Test_Parameters;
53 | begin
54 | Params.Set_Parameter ("auth.provider.google", "openid");
55 | Params.Set_Parameter ("auth.provider.openid", "openid");
56 | Params.Set_Parameter ("openid.realm", "http://localhost/verify");
57 | Params.Set_Parameter ("openid.callback_url", "http://localhost/openId");
58 | M.Initialize (Params, Name);
59 | end Setup;
60 |
61 | procedure Check_Discovery (T : in out Test;
62 | Name : in String;
63 | URI : in String) is
64 | pragma Unreferenced (URI, T);
65 |
66 | M : Manager;
67 | Dir : constant String := "regtests/files/discover/";
68 | Path : constant String := Util.Tests.Get_Path (Dir);
69 | Result : End_Point;
70 | begin
71 | Setup (M, "openid");
72 | Util.Http.Clients.Mockups.Register;
73 | Util.Http.Clients.Mockups.Set_File (Path & Name & ".xrds");
74 | M.Discover (Name => Name,
75 | Result => Result);
76 | Ada.Text_IO.Put_Line ("Result: " & To_String (Result));
77 | end Check_Discovery;
78 |
79 | -- ------------------------------
80 | -- Test Yadis discovery using static files
81 | -- ------------------------------
82 | procedure Test_Discovery (T : in out Test) is
83 | begin
84 | Check_Discovery (T, "google", "https://www.google.com/accounts/o8/ud");
85 | Check_Discovery (T, "yahoo", "https://open.login.yahooapis.com/openid/op/auth");
86 | Check_Discovery (T, "claimid", "");
87 | Check_Discovery (T, "livejournal", "");
88 | Check_Discovery (T, "myopenid", "");
89 | Check_Discovery (T, "myspace", "");
90 | Check_Discovery (T, "orange", "");
91 | Check_Discovery (T, "verisign", "");
92 | Check_Discovery (T, "steamcommunity", "");
93 | end Test_Discovery;
94 |
95 | -- ------------------------------
96 | -- Test the OpenID verify signature process
97 | -- ------------------------------
98 | procedure Test_Verify_Signature (T : in out Test) is
99 | Assoc : Association;
100 | Req : Test_Parameters;
101 | M : Manager;
102 | Result : Authentication;
103 | begin
104 | Setup (M, "openid");
105 |
106 | pragma Style_Checks ("-mr");
107 |
108 | -- Below is a part of the authentication process on Google OpenId.
109 | -- In theory, you cannot use the following information to authenticate again...
110 | Assoc.Session_Type := To_Unbounded_String ("no-encryption");
111 | Assoc.Assoc_Type := To_Unbounded_String ("HMAC-SHA1");
112 | Assoc.Assoc_Handle := To_Unbounded_String ("AOQobUdTfNDRSgJLi_0mQQnCCstOsefQadOiW9LNSp4JFO815iHCHsRk");
113 | Assoc.Mac_Key := To_Unbounded_String ("NGFpR6vWfe7O8YIhhnXQMjL0goI=");
114 |
115 | Req.Set_Parameter ("openid.ns", "http://specs.openid.net/auth/2.0");
116 | Req.Set_Parameter ("openid.mode", "id_res");
117 | Req.Set_Parameter ("openid.op_endpoint", "https://www.google.com/accounts/o8/ud");
118 | Req.Set_Parameter ("openid.response_nonce", "2011-04-26T20:08:22ZJ_neiVqR0e1wZw");
119 | Req.Set_Parameter ("openid.return_to", "http://localhost/openId");
120 | Req.Set_Parameter ("openid.assoc_handle", "AOQobUdTfNDRSgJLi_0mQQnCCstOsefQadOiW9LNSp4JFO815iHCHsRk");
121 | Req.Set_Parameter ("openid.signed", "op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,ns.ext1,ext1.mode,ext1.type.firstname,ext1.value.firstname,ext1.type.email,ext1.value.email,ext1.type.language,ext1.value.language,ext1.type.lastname,ext1.value.lastname");
122 | Req.Set_Parameter ("openid.sig", "pV8cmScjrmgKvFn2F6Wxh/qBiIE=");
123 | Req.Set_Parameter ("openid.identity", "https://www.google.com/accounts/o8/id?id=AItOawm4O6C695XlWrS7MUWC-_V_R2zC-Ol993E");
124 | Req.Set_Parameter ("openid.claimed_id", "https://www.google.com/accounts/o8/id?id=AItOawm4O6C695XlWrS7MUWC-_V_R2zC-Ol993E");
125 | Req.Set_Parameter ("openid.ns.ext1", "http://openid.net/srv/ax/1.0");
126 | Req.Set_Parameter ("openid.ext1.mode", "fetch_response");
127 | Req.Set_Parameter ("openid.ext1.type.firstname", "http://axschema.org/namePerson/first");
128 | Req.Set_Parameter ("openid.ext1.value.firstname", "Stephane");
129 | Req.Set_Parameter ("openid.ext1.type.email", "http://axschema.org/contact/email");
130 | Req.Set_Parameter ("openid.ext1.value.email", "stephane.carrez@gmail.com");
131 | Req.Set_Parameter ("openid.ext1.type.language", "http://axschema.org/pref/language");
132 | Req.Set_Parameter ("openid.ext1.value.language", "fr");
133 | Req.Set_Parameter ("openid.ext1.type.lastname", "http://axschema.org/namePerson/last");
134 | Req.Set_Parameter ("openid.ext1.value.lastname", "Carrez");
135 |
136 | M.Verify (Assoc, Req, Result);
137 |
138 | -- If the verification is succeeds, the signature is correct, we should be authenticated.
139 | T.Assert (Get_Status (Result) = AUTHENTICATED, "Authentication status is not authenticated");
140 | Util.Tests.Assert_Equals (T, "stephane.carrez@gmail.com", Get_Email (Result), "Invalid email");
141 | end Test_Verify_Signature;
142 |
143 | end Security.Auth.Tests;
144 |
--------------------------------------------------------------------------------
/src/security-oauth-jwt.adb:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------
2 | -- security-oauth-jwt -- OAuth Java Web Token
3 | -- Copyright (C) 2013, 2017 Stephane Carrez
4 | -- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
5 | -- SPDX-License-Identifier: Apache-2.0
6 | -----------------------------------------------------------------------
7 |
8 | with Ada.Calendar.Conversions;
9 | with Interfaces.C;
10 |
11 | with Util.Encoders;
12 | with Util.Strings;
13 | with Util.Serialize.IO;
14 | with Util.Properties.JSON;
15 | with Util.Log.Loggers;
16 | package body Security.OAuth.JWT is
17 |
18 | Log : constant Util.Log.Loggers.Logger := Util.Log.Loggers.Create ("Security.OAuth.JWT");
19 |
20 | function Get_Time (From : in Util.Properties.Manager;
21 | Name : in String) return Ada.Calendar.Time;
22 |
23 | -- Decode the part using base64url and parse the JSON content into the property manager.
24 | procedure Decode_Part (Into : in out Util.Properties.Manager;
25 | Name : in String;
26 | Data : in String);
27 |
28 | function Get_Time (From : in Util.Properties.Manager;
29 | Name : in String) return Ada.Calendar.Time is
30 | Value : constant String := From.Get (Name);
31 | begin
32 | return Ada.Calendar.Conversions.To_Ada_Time (Interfaces.C.long'Value (Value));
33 | end Get_Time;
34 |
35 | -- ------------------------------
36 | -- Get the issuer claim from the token (the "iss" claim).
37 | -- ------------------------------
38 | function Get_Issuer (From : in Token) return String is
39 | begin
40 | return From.Claims.Get ("iss");
41 | end Get_Issuer;
42 |
43 | -- ------------------------------
44 | -- Get the subject claim from the token (the "sub" claim).
45 | -- ------------------------------
46 | function Get_Subject (From : in Token) return String is
47 | begin
48 | return From.Claims.Get ("sub");
49 | end Get_Subject;
50 |
51 | -- ------------------------------
52 | -- Get the audience claim from the token (the "aud" claim).
53 | -- ------------------------------
54 | function Get_Audience (From : in Token) return String is
55 | begin
56 | return From.Claims.Get ("aud");
57 | end Get_Audience;
58 |
59 | -- ------------------------------
60 | -- Get the expiration claim from the token (the "exp" claim).
61 | -- ------------------------------
62 | function Get_Expiration (From : in Token) return Ada.Calendar.Time is
63 | begin
64 | return Get_Time (From.Claims, "exp");
65 | end Get_Expiration;
66 |
67 | -- ------------------------------
68 | -- Get the not before claim from the token (the "nbf" claim).
69 | -- ------------------------------
70 | function Get_Not_Before (From : in Token) return Ada.Calendar.Time is
71 | begin
72 | return Get_Time (From.Claims, "nbf");
73 | end Get_Not_Before;
74 |
75 | -- ------------------------------
76 | -- Get the issued at claim from the token (the "iat" claim).
77 | -- ------------------------------
78 | function Get_Issued_At (From : in Token) return Ada.Calendar.Time is
79 | begin
80 | return Get_Time (From.Claims, "iat");
81 | end Get_Issued_At;
82 |
83 | -- ------------------------------
84 | -- Get the authentication time claim from the token (the "auth_time" claim).
85 | -- ------------------------------
86 | function Get_Authentication_Time (From : in Token) return Ada.Calendar.Time is
87 | begin
88 | return Get_Time (From.Claims, "auth_time");
89 | end Get_Authentication_Time;
90 |
91 | -- ------------------------------
92 | -- Get the JWT ID claim from the token (the "jti" claim).
93 | -- ------------------------------
94 | function Get_JWT_ID (From : in Token) return String is
95 | begin
96 | return From.Claims.Get ("jti");
97 | end Get_JWT_ID;
98 |
99 | -- ------------------------------
100 | -- Get the authorized clients claim from the token (the "azp" claim).
101 | -- ------------------------------
102 | function Get_Authorized_Presenters (From : in Token) return String is
103 | begin
104 | return From.Claims.Get ("azp");
105 | end Get_Authorized_Presenters;
106 |
107 | -- ------------------------------
108 | -- Get the claim with the given name from the token.
109 | -- ------------------------------
110 | function Get_Claim (From : in Token;
111 | Name : in String;
112 | Default : in String := "") return String is
113 | begin
114 | return From.Claims.Get (Name, Default);
115 | end Get_Claim;
116 |
117 | -- ------------------------------
118 | -- Decode the part using base64url and parse the JSON content into the property manager.
119 | -- ------------------------------
120 | procedure Decode_Part (Into : in out Util.Properties.Manager;
121 | Name : in String;
122 | Data : in String) is
123 | Decoder : constant Util.Encoders.Decoder := Util.Encoders.Create (Util.Encoders.BASE_64_URL);
124 | Content : constant String := Decoder.Decode (Data);
125 | begin
126 | Log.Debug ("Decoding {0}: {1}", Name, Content);
127 |
128 | Util.Properties.JSON.Parse_JSON (Into, Content);
129 | end Decode_Part;
130 |
131 | -- ------------------------------
132 | -- Decode a string representing an encoded JWT token according to the JWT specification:
133 | --
134 | -- Section 7. Rules for Creating and Validating a JWT
135 | --
136 | -- The JWT token is composed of 3 parts encoded in Base64url and separated by '.' .
137 | -- The first part represents the header, the second part the claims and the last part
138 | -- the signature. The Decode operation splits the parts, decodes them,
139 | -- parses the JSON content represented by the header and the claims.
140 | -- The Decode operation does not verify the signature (yet!).
141 | --
142 | -- Return the decoded token or raise an exception.
143 | -- ------------------------------
144 | function Decode (Content : in String) return Token is
145 | Pos1 : constant Natural := Util.Strings.Index (Content, '.');
146 | Pos2 : Natural;
147 | Result : Token;
148 | begin
149 | if Pos1 = 0 then
150 | Log.Error ("Invalid JWT token: missing '.' separator. JWT: {0}", Content);
151 | raise Invalid_Token with "Missing header separator";
152 | end if;
153 | Pos2 := Util.Strings.Index (Content, '.', Pos1 + 1);
154 | if Pos2 = 0 then
155 | Log.Error ("Invalid JWT token: missing second '.' separator. JWT: {0}", Content);
156 | raise Invalid_Token with "Missing signature separator";
157 | end if;
158 | Decode_Part (Result.Header, "header", Content (Content'First .. Pos1 - 1));
159 | Decode_Part (Result.Claims, "claims", Content (Pos1 + 1 .. Pos2 - 1));
160 | return Result;
161 |
162 | exception
163 | when Util.Serialize.IO.Parse_Error =>
164 | raise Invalid_Token with "Invalid JSON content";
165 | end Decode;
166 |
167 | end Security.OAuth.JWT;
168 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ada Security Library
2 |
3 | [](https://alire.ada.dev/crates/security)
4 | [](https://adaic.org/ada-resources/standards/ada12)
5 | [](https://porion.vacs.fr/porion/projects/view/ada-security/summary)
6 | [](https://porion.vacs.fr/porion/projects/view/ada-securit/xunits)
7 | [](https://porion.vacs.fr/porion/projects/view/ada-security/summary)
8 | [](https://ada-security.readthedocs.io/en/latest/?badge=latest)
9 | [](http://download.vacs.fr/ada-security/ada-security-1.5.1.tar.gz)
10 | [](LICENSE)
11 | [](https://gitlab.com/stcarrez/ada-security)
12 | [](Commits)
13 |
14 | Ada Security provides a security framework which allows applications to define
15 | and enforce security policies. This framework allows users to authenticate by using
16 | [OpenID Authentication 2.0](https://openid.net/specs/openid-authentication-2_0.html)
17 | as well as [OAuth 2.0](https://oauth.net/2/) protocol.
18 | It allows a web application to integrate easily with Yahoo!, Gitlab, Github, Facebook and
19 | Google+ authentication systems.
20 | The Ada05 library includes:
21 |
22 | * An OpenID client authentication,
23 | * An OAuth 2.0 client authentication,
24 | * An OpenID Connect authentication framework,
25 | * An OAuth 2.0 server authentication framework,
26 | * A policy based security framework to protect the resources
27 |
28 | 
29 |
30 | The Ada Security library is used by the
31 | [Ada Web Application](https://gitlab.com/stcarrez/ada-awa)
32 | and the [OpenAPI Ada library](https://github.com/stcarrez/swagger-ada)
33 | to provide authentication and access control to users within the web applications.
34 |
35 | ## Version 1.5.2 - Under development
36 | - Fix #11: Ada.Calendar.Conversions.To_Struct_Timespec is deprecated
37 |
38 | ## Version 1.5.1 - Aug 2024
39 | - Cleanup build environment to drop configure
40 |
41 | [List all versions](https://gitlab.com/stcarrez/ada-security/blob/master/NEWS.md)
42 |
43 | ## Using with Alire
44 |
45 | If you are using [Alire](https://alire.ada.dev/) in your project, run the following command
46 | within your [Alire](https://alire.ada.dev/) project to use the library:
47 |
48 | ```
49 | alr with security
50 | ```
51 |
52 | ## Using without Alire
53 |
54 | If you don't have [Alire](https://alire.ada.dev/) or want to build and install the library
55 | on a specific place, run a `setup` command to configure the build as well as installation
56 | directory.
57 | For a detailed description on how you can configure, build and install the library
58 | refer to the [Installation](https://ada-security.readthedocs.io/en/latest/Installation/) guide.
59 | Otherwise, you can easily configure and build the library with the steps described below.
60 |
61 | The `HAVE_ALIRE` configuration allows you to disable the build with [Alire](https://alire.ada.dev/):
62 |
63 | ```
64 | make setup BUILD=debug PREFIX=/build/install HAVE_ALIRE=no
65 | ```
66 |
67 | Since this build method does not verify that all dependencies are met, make sure that you
68 | have already built and install the following components and they are available to `gprbuild`
69 | through `ADA_PROJECT_PATH` if needed:
70 |
71 | * [Ada Utility Library](https://gitlab.com/stcarrez/ada-util/)
72 |
73 | Then build, run the unit tests and install by using:
74 |
75 | ```
76 | make
77 | make test
78 | make install
79 | ```
80 |
81 | To use the installed libraries, make sure your `ADA_PROJECT_PATH` contains the directory
82 | where you installed the libraries (configured by the `PREFIX=` option in the setup phase).
83 | The installed GNAT projects are the same as those used when using [Alire](https://alire.ada.dev/).
84 |
85 |
86 | ## Running the tests
87 |
88 | The unit tests are built and executed with:
89 |
90 | ```
91 | make test
92 | ```
93 |
94 | ## Samples
95 |
96 | The package provides a simple AWS server that illustrates the OpenID and OpenConnect
97 | authentication. Because OAuth2 relies exclusively on HTTPS, you must use an AWS
98 | server that has the SSL support. Build it as follows:
99 |
100 | ```
101 | cd samples
102 | alr build
103 | ```
104 |
105 | Before launching the demo server, you must update the 'samples.properties' file
106 | and change the lines that contain PUT-HERE-YOUR-FACEBOOK-xxx with your client ID
107 | and client secrets. This change
108 | is required by the OAuth and OpenID Connect framework only.
109 | Then, run the server:
110 | ```
111 | bin/auth_demo
112 | ```
113 | and redirect your browser to:
114 | ```
115 | http://localhost:8080/atlas/login.html
116 | ```
117 | # Documentation
118 |
119 | The Ada Security sources as well as a wiki documentation is provided on:
120 |
121 | - [Ada Security Programmer's Guide](https://ada-security.readthedocs.io/en/latest/)
122 | - [Overview](https://gitlab.com/stcarrez/ada-security/wiki)
123 | - [Security Overview](https://gitlab.com/stcarrez/ada-security/wiki/Security)
124 |
125 |
126 | # Other Documentation
127 |
128 | The OAuth literature is quite complete on the Internet and there are several good tutorials and
129 | documentation.
130 | - [Facebook Login](https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow)
131 | - [Using OAuth 2.0 to Access Google APIs](https://developers.google.com/identity/protocols/OAuth2)
132 | - [Yahoo OAuth 2.0 Guide](https://developer.yahoo.com/oauth2/guide/)
133 | - [Salesforce OAuth 2.0 Guide](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_understanding_authentication.htm)
134 | (this is a good guide if you want to learn)
135 |
136 | # References
137 |
138 | - [RFC 6749: The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
139 | - [RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750)
140 | - [RFC 6819: OAuth 2.0 Threat Model and Security Considerations](https://tools.ietf.org/html/rfc6819)
141 | - [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)
142 |
143 | # Sites Using Ada Security
144 |
145 | * [Java 2 Ada](https://blog.vacs.fr/)
146 | * [Ada France](https://www.ada-france.org/adafr/index.html)
147 | * [Atlas](https://demo.vacs.fr/atlas/index.html)
148 | * [Jason Project Manager](https://vdo.vacs.fr/vdo/index.html)
149 | * [Porion Build Manager](https://porion.vacs.fr/porion/index.html)
150 |
--------------------------------------------------------------------------------