#include <stdio.h>
#include <yyast/types.h>
Go to the source code of this file.
Defines | |
| #define | _GNU_SOURCE |
| #define | YA_BRANCH(name,...) ya_branch(name, __VA_ARGS__, NULL) |
| Define a new node. | |
| #define | YA_EMPTYBRANCH(name) ya_branch(name, NULL) |
| A branch that is empty. | |
| #define | YA_LIST(...) ya_list("@list", __VA_ARGS__, NULL) |
| Define a list of nodes. | |
| #define | YA_EMPTYLIST ya_list("@list", NULL) |
| An empty list contains no nodes, but can be used to start a sequence of nodes in the grammar. | |
Functions | |
| ya_t | ya_branch (const char *restrict name,...) |
| Create an ya node. | |
| ya_t | ya_list (const char *restrict name,...) |
| Create an ya node. | |
| void | ya_node_save (FILE *output_file, ya_t *node) |
| Save the node to a file. |
| #define YA_BRANCH | ( | name, | |||
| ... | ) | ya_branch(name, __VA_ARGS__, NULL) |
Define a new node.
Used in a yacc action to start a new node. If a list is added to a branch, then the nodes of the list are added to the created node, instead of the list itself.
| name | Name of the node | |
| ... | A set of nodes, leaves or lists to be included in this node. |
Definition at line 41 of file node.h.
Referenced by ya_get_filenames(), and ya_header().
| #define YA_EMPTYBRANCH | ( | name | ) | ya_branch(name, NULL) |
| #define YA_EMPTYLIST ya_list("@list", NULL) |
An empty list contains no nodes, but can be used to start a sequence of nodes in the grammar.
Definition at line 62 of file node.h.
Referenced by ya_get_filenames().
| #define YA_LIST | ( | ... | ) | ya_list("@list", __VA_ARGS__, NULL) |
Define a list of nodes.
Used in yacc to create a list of nodes. When adding a list to an other list or node, the contents of the list is appended to the node and list.
| ... | A list of literals to be included in this node. |
Definition at line 58 of file node.h.
Referenced by ya_get_filenames().
| ya_t ya_branch | ( | const char *restrict | name, | |
| ... | ||||
| ) |
Create an ya node.
Create a node from subnodes. This will free memory used by the subnodes. If a list is added to a branch, then the nodes of the list are added to the created node, instead of the list itself.
| name | Name of the node. | |
| ... | The subnodes, ending with NULL |
Definition at line 151 of file node.c.
References ya_generic_nodev(), and YA_NODE_TYPE_BRANCH.
| ya_t ya_list | ( | const char *restrict | name, | |
| ... | ||||
| ) |
Create an ya node.
Create a list of subnodes, when included in a list or node, it expands as if the contents was passed as seperate items. This also free memory used by the subnodes.
| name | Name of the node. This should be '@list' because this function is called from the YA_LIST macro. | |
| ... | The subnodes, ending with NULL |
Definition at line 163 of file node.c.
References ya_generic_nodev(), and YA_NODE_TYPE_LIST.
| void ya_node_save | ( | FILE * | output_file, | |
| ya_t * | node | |||
| ) |
Save the node to a file.
| output_file | A file pointer of an file open for writing. | |
| node | The node to be saved to file. |
Definition at line 175 of file node.c.
References ya_t::node, and ya_t::size.
Referenced by ya_main().


{% extends "base.html" %} {% block content %}
{{survey.summary}}
It will take few minutes, share your opinion with us


boost::process::posix_child — POSIX implementation of the Child concept.
class posix_child : public boost::process::child { public: // construct/copy/destruct posix_child(handle_type, unspecified, unspecified); // public member functions postream & get_input(int) const; pistream & get_output(int) const; };
The posix_child class implements the Child concept in a POSIX operating system.
 These channels are identified by regular file descriptors (integers).
This class is built on top of the generic child so as to allow its trivial adoption. When a program is changed to use the POSIX-specific launcher (posix_launcher), it will most certainly need to migrate its use of the child class to posix_child. Doing so is only a matter of redefining the appropriate object and later using the required extra features: there should be no need to modify the existing code (e.g. method calls) in any other way.
posix_child
construct/copy/destructposix_child(handle_type h, unspecified infoin, unspecified infoout);
Creates a new child object that represents the just spawned process h.
The infoin and infoout maps contain the pipes used to handle the redirections of the child process; at the moment, no other stream_info types are supported. If the launcher was asked to redirect any of the three standard flows, their pipes must be present in these maps.
This constructor is protected because the library user has no business in creating representations of live processes himself; the library takes care of that in all cases.
posix_child
public member functionspostream & get_input(int desc) const;
Returns a reference to a postream object that represents one of the multiple input communication channels with the child process. The channel is identified by desc as seen from the child's point of view. The parent can use the returned stream to send data to the child.
Giving this function the STDIN_FILENO constant (defined in cstdlib) is a synonym for the get_stdin() call inherited from child.
pistream & get_output(int desc) const;
Returns a reference to a pistream object that represents one of the multiple output communication channels with the child process. The channel is identified by desc as seen from the child's point of view. The parent can use the returned stream to retrieve data from the child.
Giving this function the STDOUT_FILENO or STDERR_FILENO constants (both defined in cstdlib) are synonyms for the get_stdout() and get_stderr() calls inherited from child, respectively.
| Copyright © 2006 Julio M. Merino Vidal |


◆ __init__()
| def assign_sections_process.AssignSectionsProcess.__init__ |
( |
|
self, |
|
|
|
Model, |
|
|
|
custom_settings |
|
) |
| |
◆ Execute()
| def assign_sections_process.AssignSectionsProcess.Execute |
( |
|
self | ) |
|
◆ ExecuteFinalize()
| def assign_sections_process.AssignSectionsProcess.ExecuteFinalize |
( |
|
self | ) |
|
◆ ExecuteInitialize()
| def assign_sections_process.AssignSectionsProcess.ExecuteInitialize |
( |
|
self | ) |
|
◆ echo_level
| assign_sections_process.AssignSectionsProcess.echo_level |
◆ material_name
| assign_sections_process.AssignSectionsProcess.material_name |
◆ model_part
| assign_sections_process.AssignSectionsProcess.model_part |
◆ properties
| assign_sections_process.AssignSectionsProcess.properties |
◆ settings
| assign_sections_process.AssignSectionsProcess.settings |
settings string in json format
◆ tables
| assign_sections_process.AssignSectionsProcess.tables |
◆ variables
| assign_sections_process.AssignSectionsProcess.variables |
The documentation for this class was generated from the following file:
- /home/runner/work/Documentation/Documentation/master/applications/SolidMechanicsApplication/python_scripts/assign_sections_process.py


|
ESP32 HTTPS Server
|
Main implementation for the plain HTTP server. Use HTTPSServer for TLS support. More...
#include <HTTPServer.hpp>
Public Member Functions | |
| HTTPServer (const uint16_t portHTTPS=80, const uint8_t maxConnections=8, const in_addr_t bindAddress=0) | |
| uint8_t | start () |
| void | stop () |
| bool | isRunning () |
| void | loop () |
| void | setDefaultHeader (std::string name, std::string value) |
| Public Member Functions inherited from httpsserver::ResourceResolver | |
| void | registerNode (HTTPNode *node) |
| void | unregisterNode (HTTPNode *node) |
| void | setDefaultNode (HTTPNode *node) |
| void | resolveNode (const std::string &method, const std::string &url, ResolvedResource &resolvedResource, HTTPNodeType nodeType) |
| void | addMiddleware (const HTTPSMiddlewareFunction *mwFunction) |
| void | removeMiddleware (const HTTPSMiddlewareFunction *mwFunction) |
| const std::vector< HTTPSMiddlewareFunction * > | getMiddleware () |
Protected Member Functions | |
| virtual uint8_t | setupSocket () |
| virtual void | teardownSocket () |
| virtual int | createConnection (int idx) |
Protected Attributes | |
| const uint16_t | _port |
| const uint8_t | _maxConnections |
| const in_addr_t | _bindAddress |
| HTTPConnection ** | _connections |
| boolean | _running |
| int | _socket |
| sockaddr_in | _sock_addr |
| HTTPHeaders | _defaultHeaders |
Main implementation for the plain HTTP server. Use HTTPSServer for TLS support.
| void httpsserver::HTTPServer::loop | ( | ) |
The loop method can either be called by periodical interrupt or in the main loop and handles processing of data
| void httpsserver::HTTPServer::setDefaultHeader | ( | std::string | name, |
| std::string | value | ||
| ) |
Adds a default header that is included in every response.
This could be used for example to add a Server: header or for CORS options
|
protectedvirtual |
This method prepares the tcp server socket
| uint8_t httpsserver::HTTPServer::start | ( | ) |
This method starts the server and begins to listen on the port
| void httpsserver::HTTPServer::stop | ( | ) |
This method stops the server


| 1.4.3 | New gpa.js algorithm. |
| 1.4.1 | Updated for 1-7 grade scale. Added old GPA for seniors. |
| 1.3.9 | Minor visual tweaks. |
| 1.3.8 | Added percentages. |
| 1.3.7 | Minor usability tweaks. |
| 1.3.6 | Fix issue where null grades would be referenced. |
| 1.3.5 | Fixing PS "Bold" Update. |
| 1.3.4 | S2 update. |
| 1.3.3 | Fix due to PS update. |
| 1.3.2 | Extension hotfix. |
| 1.3.0 | Fixed 2016-2017 GPA Calculation. Rewrote algorithm, made gpa.js more classy. |
| 1.2.5 | 2016-2017 S1 update. |
| 1.2.3 | Fixed GPA calculation issues due to website update. |
| 1.2.2 | Reorganized GPA display. Fixed minor visual errors. |
| 1.2.1 | Fixed GPA calculation in certain one-semester weighted courses. Corrected minor code issues. |
| 1.2.0 | Fixed GPA calculation for S2. Now shows S1 GPA and semester GPA. |
| 1.1.1 | Updated for S2 |
| 1.1.0 | Moved schedule to grades page. Styled schedule to make it look better. |
| 1.0.0 | Updated extension to work with new PowerSchool. Redesigned GPA calculation, should be faster. Added more PowerSchool styling. |
| 0.9.8 | Fixed GPA calculation when having an "F". |
| 0.9.7 | Removed report card styling for upcoming S2 report card. |
| 0.9.6 | GPA Calculation hotfix. |
| 0.9.5 | Redesigned GPA calculation algorithm & parsing. Fixes all previous issues with GPA calculation including non-integral grades breaking the program. |
| 0.9.0 | Styled PowerSchool home page. |
| 0.8.0 | Fixed non-integral GPA, now rounds to the nearest number. |
| 0.7.1 | Fixed enhanced schedule problems. Styled login page. |
| 0.6.0 | Moved GPA back to original place. Addition of Enhanced Schedule. |
| 0.4.1 | Added support for showing S1 & S2 GPA at the same time while in S2. Works on all PowerSchool Guardian pages now. Fixed PowerSchool mispelling. |
| 0.3.3 | Public Release: Stylized GPA. |
| 0.3.2 | Fixed bug with algorithm that mixes up S1 & 2 GPA |
| 0.3.1 | Fixed bug that made 100% grade look like 0%. |
| 0.3.0 | Developed new algorithm using hsgrades.html |
| 0.1.0 | Public Beta Release |


Before Change
assert input.size(0) == factor.size(0), \
f"Input batch size shall match with factor size if 1d array. Got {input.size(0)} and {factor.size(0)}"
else:
factor = float(factor)
kernel = torch.tensor([
[1, 1, 1],
[1, 5, 1],
[1, 1, 1]
After Change
[ 0.6181, -0.4128, -0.8411, -2.3160, -0.1023]]]])
input = _to_bchw(input)
if not isinstance(factor, torch.Tensor):
factor = torch.tensor(factor, device=input.device, dtype=input.dtype)
if len(factor.size()) != 0:
assert factor.shape == torch.Size([input.size(0)]), (
"Input batch size shall match with factor size if factor is not a 0-dim tensor. "
f"Got {input.size(0)} and {factor.shape}")


Corporate Redesign and the Business of Knowledge (p. 150-165) in Design and Truth by Robert Grudin.
In this chapter, Grudin describes arguments over imbuing corporate free market endeavors with moral imperatives, one of which is truthfulness. He also draws correlations between communication of knowledge within a company and its health. He examines Google as a case study in corporate knowledge investment and makes the case for the Chief Knowledge Officer. Let's say you were hired, just as Grudin was, to be a consultant for a large company. In what situation would you make the case for a Chief Knowledge Officer? How would you try to convince the CEO to take the idea seriously?


Hyunchul Yi(Jack)
3D Portfolio
LINK
Portfolio
Linkedin
EMail
- I have been working on 3D Artist for 10 Years such as Character, vehicle, weapons, power station, airplane, and props and now I am working on VR Game environment modeling at Sanzaru Games Which I have been making for interior props and exterior by Maya with ZBrush and I play with test in VR Oculus in Unreal Engine.
I finished for Marvel powers united 2017 and I just finished on Wrath for VR Oculus.
Now I work at Turn 10 studio as a 3D vehicle a artist in Redmond WA.
- I worked at Houzz as a 3D Artist in 2016
Working on Interior Props in Maya and play in Unity.
Worked closely with engineer to solve any issue.
I worked at BigFish Games as a 3D Artist for 4 years.
Modeling in Maya with rigging and animation.
Return to Ravenhearst with many games before that with Jeff.
-
I have been used Maya for 9 years and I create clean modelings for topology with retopology in game engine which I convert from high poly to low poly.
Texturing with PBR Shader by Ddo and Substance for new generation.
I graduated at the Academy of Art University and majored in 3D Animation.
I am really self motivation and team player.


shorthand is a command line program run from a shell like Bash.
You can find compiled version in the releases
The following curl command can be used to run the installer on most POSIX systems. Programs are installed into $HOME/bin
. $HOME/bin
will need to be in your path. From a shell (or terminal session) run the following.
curl https://rsdoiel.github.io/shorthand/installer.sh | sh
This is generalized instructions for a release.
Compiled versions are available for Mac OS X (Intel processor, macOS-x86_64), Linux (Intel process, linux-x86_64), Windows (Intel processor, Windows-x86_64), Rapsberry Pi (arm7 processor, RaspberryPiOS-arm7) and Pine64 (arm64 processor, Linux-aarch64)
VERSION_NUMBER is a symantic version number (e.g. v0.1.2)
For all the released version go to the project page on Github and click latest release
https://github.com/rsdoiel/shorthand/releases/latest
| Platform | Zip Filename |
|---|---|
| Windows | shorthand-VERSION_NUMBER-Windows-x86_64.zip |
| Mac OS X | shorthand-VERSION_NUMBER-macOS-x86_64.zip |
| Linux/Intel | shorthand-VERSION_NUMBER-Linux-x86_64.zip |
| Raspbery Pi | shorthand-VERSION_NUMBER-RaspberryPiOS-arm7.zip |
| Pine64 | shorthand-VERSION_NUMBER-Linux-aarch64.zip |
Here’s an example of the commands run in the Terminal App after downloading the zip file in to your “Downloads” folder.
cd
unzip Downloads/shorthand-*-macOS-x86_64.zip bin/*
export PATH="$HOME/bin:$PATH"
shorthand -version
Here’s an example of the commands run in from the Bash shell on Windows 10 after downloading the zip file.
cd
unzip Downloads/shorthand-*-Windows-x86_64.zip bin/*
export PATH="$HOME/bin:$PATH"
shorthand -version
Here’s an example of the commands run in from the Bash shell after downloading the zip file.
cd
unzip Downloads/shorthand-*-Linux-x86_64.zip bin/*
export PATH="$HOME/bin:$PATH"
shorthand -version
Released version is for a Raspberry Pi 2 or later use (i.e. requires ARM 7 support).
Here’s an example of the commands run in from the Bash shell after downloading the zip file.
cd
unzip Downloads/shorthand-*-RaspberryPiOS-arm7.zip bin/*
export PATH="$HOME/bin:$PATH"
shorthand -version
shorthand is “go gettable”. Use the “go get” command to download the dependant packages as well as shorthand’s source code.
go get -u github.com/rsdoiel/shorthand/...
Or clone the repository and then compile
cd
git clone https://github.com/rsdoiel/shorthand src/github.com/rsdoiel/shorthand
cd src/github.com/rsdoiel/shorthand
make
make test
make install


Debt History
| Debt reason | Date Accrued | Amount £ |
|---|---|---|
| Social Loan | 12-12-2013 | 1,700 |
| Benefit Overpayment, Income Support | 06-11-2015 | 340 |
| Benefit Overpayment, Job Seekers Allowance | 06-11-2015 | 250 |
| Debt reason | Date Accrued | Amount £ |
|---|---|---|
| Social Loan | 12-12-2013 | 1,700 |
| Benefit Overpayment, Income Support | 06-11-2015 | 340 |
| Benefit Overpayment, Job Seekers Allowance | 06-11-2015 | 250 |


#parse("/de/fct/companian/web/site/inc/header.htm")
Binding Capability: ${binding.name}
${binding.description}
API Calls
#foreach ( ${apiCall} in ${binding.bindingDescription.apiCalls} )
- ${apiCall.methodAPIPath}
${apiCall.description}
#end
- Add new and enter the API documentation path
#parse("/de/fct/companian/web/site/inc/footer.htm")


5 things we spotted from Freddie Ljungberg's first training session as Arsenal boss
The Swede took charge of his first session since replacing Unai Emery in the hotseat, with the Spaniard having been sacked on Friday morning
by Irishmirror.ieFreddie Ljungberg got straight down to work on his first day as Arsenal's new interim manager, as the Swede prepared his side for the weekend trip to Norwich.
Friday morning's news that Unai Emery had been sacked after 18 months in charge at the Emirates Stadium came as a surprise to no-one, especially after Arsenal's 2-1 defeat at home to Eintracht Frankfurt on Thursday night.
Former Gunners star Ljungberg has stepped into the breach in an interim role, with the likes of Nuno Espirito Santo, Carlo Ancelotti, Mikel Arteta and Max Allegri all linked with taking the job on a permanent basis.
Ljungberg will see this as a perfect chance to make his mark on the role though, and here are some images from his first training session as the No.1.
1. Hello again
While this is a new appointment, of course Ljungberg isn't a new presence around London Colney.
The Swede is the former boss of the under-23s and served as Emery's assistant this season, and it was no surprise that he made a beeline for the club's younger players at the session, including Robbie Burton here.
Many will hope to be involved in the coming weeks.
2. Pulling together
Arsenal's squad has seemed pretty broken in recent weeks, and so Ljungberg's greatest task will be to restore some unity at the club.
With claims that players were mocking Emery towards the end of his reign, the club's former midfielder surely deserves more respect as he bids to turn things around.
3. On the attack
He was an exciting, attacking player, and so plenty of people are expecting Ljungberg's Arsenal to be an exciting, attacking outfit when they get down to business at Norwich on Sunday.
He might want to pay a bit more attention to defence than he is in this picture though.
4. Plotting changes
Ljungberg's first training session was purely for players who didn't start the Frankfurt defeat. and so gave him the perfect opportunity to plot changes for the weekend trip to Norwich.
The Swede spoke to Lucas Torreira and Reiss Nelson as he sought to get his points across, and his team selection for Sunday certainly will be interesting.
5. New manager, same Ozil?
We're sure he ran around a bit .
He must have done, right?


See Best Practices for additional information.
The value can contain environment variables in the form of ${VARIABLE_NAME} which are resolved.
${VARIABLE_NAME}


lpc
Section: Apple Inc. (8)Updated: CUPS
Index Return to Main Contents
NAME
lpc - line printer control programSYNOPSIS
lpc [ command [ parameter(s) ] ]DESCRIPTION
lpc provides limited control over printer and class queues provided by CUPS. It can also be used to query the state of queues.If no command is specified on the command-line, lpc will display a prompt and accept commands from the standard input.
COMMANDS
The lpc program accepts a subset of commands accepted by the Berkeley lpc program of the same name:- exit
-
Exits the command interpreter. - help [command]
- ? [command]
-
Displays a short help message. - quit
-
Exits the command interpreter. - status [queue]
-
Displays the status of one or more printer or class queues.
LIMITATIONS
Since lpc is geared towards the Berkeley printing system, it is impossible to use lpc to configure printer or class queues provided by CUPS. To configure printer or class queues you must use the lpadmin(8) command or another CUPS-compatible client with that functionality.COMPATIBILITY
The CUPS version of lpc does not implement all of the standard Berkeley or LPRng commands.SEE ALSO
cancel(1), cupsaccept(8), cupsenable(8), lp(1), lpr(1), lprm(1), lpstat(1),http://localhost:631/help
COPYRIGHT
Copyright 2007-2009 by Apple Inc.
Index
This document was created by man2html, using the manual pages.
Time: 05:34:27 GMT, December 24, 2015


Writing Secure Ballerina Code
The sections below include information on the different security features and controls available within Ballerina. Also, they provide guidelines on writing secure Ballerina programs.
- Secure by Design
- Securing Passwords and Secrets
- Authentication and Authorization
- Inbound Authentication and Authorization
- Outbound Authentication and Authorization
Secure by Design
This approach makes it unnecessary for developers to review best practice coding lists that itemize how to avoid security vulnerabilities. The Ballerina compiler ensures that Ballerina programs do not introduce security vulnerabilities.
A taint analysis mechanism is used to achieve this.
Parameters in function calls can be designated as security-sensitive. The compiler will generate an error if you pass untrusted data (tainted data) into a security-sensitive parameter:
tainted value passed to sensitive parameter 'sqlQuery'
We require developers to explicitly mark all values passed into security-sensitive parameters as ‘trusted’. This explicit check forces developers and code reviewers to verify that the values being passed into the parameter are not vulnerable to a security violation.
Ballerina standard library makes sure untrusted data cannot be used with security-sensitive parameters such as SQL queries, file paths, file name, permission flags, request URLs and configuration keys preventing vulnerabilities including:
- SQL Injection
- Path Manipulation
- File Manipulation
- Unauthorized File Access
- Unvalidated Redirect (Open Redirect)
Ensuring Security of Ballerina Standard Libraries
Security-sensitive functions and remote methods of Ballerina standard libraries are annotated with the @untainted
parameter annotation. This denotes that untrusted (tainted) data should not be passed to the parameter.
For example, the sqlQuery
parameter of the ballerinax/java.jdbc
select
remote method is annotated as @untainted
.
public remote function select(@untainted string sqlQuery,
typedesc<record{}>? recordType,
Param... parameters)
returns @tainted table<record {}>|Error
The following example constructs an SQL query with a tainted argument:
import ballerinax/java.jdbc;
type ResultStudent record {
string name;
};
public function main(string... args) {
jdbc:Client testDB = new({
url: "jdbc:mysql://localhost:3306/testdb",
username: "test",
password: "test",
poolOptions: { maximumPoolSize: 5 },
dbOptions: { useSSL: false }
});
// Construct student ID based on user input.
string studentId = "S_" + args[0];
// Execute select query using the untrusted (tainted) student ID
var dt = testDB->select("SELECT NAME FROM STUDENT WHERE ID = " + studentId,
ResultStudent);
testDB.stop();
}
The Ballerina compiler will generate an error:
tainted value passed to sensitive parameter 'sqlQuery'
In order to compile, the program is modified to use query parameters:
jdbc:Parameter paramId = {sqlType:jdbc:TYPE_VARCHAR, value:studentId};
var dt = testDB->select("SELECT NAME FROM STUDENT WHERE ID = ?", ResultStudent,
paramId);
Command-line arguments passed to Ballerina programs and inputs received through service resources are considered as tainted. Additionally, return values of certain functions are marked with the @tainted
annotation to denote that the resulting value should be considered untrusted data.
For example, the select
remote method of the java:jdbc
client highlighted above returns a @tainted table<record {}>|Error
. This means that any value read from a database is considered untrusted.
When the Ballerina compiler can determine that a function is returning tainted data without tainted data being passed in as parameters to that function, it is required to annotate the function’s return type as @tainted
. If not, the function author has to clean up the data before returning it. For instance, if you are to read from the database and return that result, you either need to annotate that function’s return type as @tainted
or you have to clean up and make sure the returned data is not tainted.
Securely Using Tainted Data with Security-Sensitive Parameters
There can be certain situations where a tainted value must be passed into a security-sensitive parameter. In such situations, it is essential to do proper data validation or data sanitization to make sure the input does not result in a security threat. Once proper controls are in place, the @untainted
annotation can be used with a type cast operator to denote that the value is trusted:
// Execute select query using the untrusted (tainted) student ID
boolean isValid = isNumeric(studentId);
if (isValid) {
var dt = testDB->select("SELECT NAME FROM STUDENT WHERE ID = " +
<@untainted> studentId, ResultStudent);
}
// ...
Additionally, return values can be annotated with@untainted
. This denotes that the return value should be trusted (even if the return value is derived from tainted data):
// Execute the select query using the untrusted (tainted) student ID
function sanitizeSortColumn (string columnName) returns @untainted string {
string sanitizedSortColumn = columnName;
// Insert sanitization logic to ensure that the return value is safe.
return sanitizedSortColumn;
}
// ...
Securing Passwords and Secrets
Ballerina provides an API to access configuration values from different sources. For more information, see Config Ballerina by Example.
Configuration values containing passwords or secrets should be encrypted. The Ballerina Config API will decrypt such configuration values when being accessed.
Use the following command to encrypt a configuration value:
$ ballerina encrypt
The encrypt command will prompt for the plain-text value to be encrypted and an encryption secret.
$ ballerina encrypt
Enter value:
Enter secret:
Re-enter secret to verify:
Add the following to the configuration file:
<key>="@encrypted:{hcBLnR+b4iaGS9PEtCMSQOUXJQTQo+zknNxCkpZ0t7w=}"
Or provide it as a command line argument:
--<key>=@encrypted:{hcBLnR+b4iaGS9PEtCMSQOUXJQTQo+zknNxCkpZ0t7w=}
Ballerina uses AES, CBC mode with PKCS#5 padding for encryption. The generated encrypted value should be used in place of the plain-text configuration value.
For example, contents of a configuration file that includes a secret value should look as follows:
api.secret="@encrypted:{hcBLnR+b4iaGS9PEtCMSQOUXJQTQo+zknNxCkpZ0t7w=}"
api.provider="not-a-security-sensitive-value"
When running a Ballerina program that uses encrypted configuration values, Ballerina will require the secret used during the encryption process to perform the decryption.
Ballerina will first look for a file named secret.txt
. If such a file exists, Ballerina will read the decryption secret from the file and immediately remove the file to make sure the secret cannot be accessed afterward. If the secret file is not present, the Ballerina program will prompt for the decryption secret.
The file based approach is useful in automated deployments. The file containing the decryption secret can be deployed along with the Ballerina program. The name and the path of the secret file can be configured using the ballerina.config.secret
runtime parameter:
$ ballerina run --b7a.config.secret=path/to/secret/file securing_configuration_values.bal
Authentication and Authorization
Inbound Authentication and Authorization
Ballerina HTTP services can be configured to enforce authentication and authorization. Ballerina has built-in support for the following inbound authentication mechanisms whereas it is possible to add custom mechanisms:
- Basic authentication
- JWT authentication
- OAuth2 authentication
- LDAP authentication
Ballerina inbound authentication is abstracted out into 2 layers called http:InboundAuthHandler
and auth:InboundAuthProvider
.
The auth:InboundAuthProvider
is a protocol-independent entity that only knows how to authenticate a user when the necessary information is provided. The http:InboundAuthHandler
can be protocol dependent. Even-though the current focus is on HTTP, the ballerina/auth
module can operate with other protocols as well.
The http:InboundAuthHandler
is used to perform HTTP-level actions, which are extracting the required HTTP header or body, extracting the credentials out of it, passing them into the associated auth:InboundAuthProvider
, and getting the credentials validated. The auth:InboundAuthProvider
is used to validate the credentials passed by the http:InboundAuthHandler
.
In a particular authentication scheme, the implemented instance of the auth:InboundAuthProvider
is initialized with the required configurations and it is passed to the implemented instance of the http:InboundAuthHandler
.
Next, the implemented instance of the http:InboundAuthHandler
is passed to the http:Listener
configuration as follows, and the listener is initialized with authentication.
The following example represents how a listener is secured with Basic Auth with the above-mentioned configurations.
import ballerina/auth;
import ballerina/http;
import ballerina/config;
auth:InboundBasicAuthProvider basicAuthProvider = new;
http:BasicAuthHandler basicAuthHandler = new(basicAuthProvider);
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [basicAuthHandler]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
service helloWorld on secureHelloWorldEp {
// ....
}
Note: It is a must to use HTTPS when enforcing authentication and authorization checks, to ensure the confidentiality of sensitive authentication data.
Optionally, the scopes
attribute is configured for the authorization as follows. If it is not specified, that means the service is authorized for any authenticated user.
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [authHandler],
scopes: ["test-scope"]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
service helloWorld on secureHelloWorldEp {
// ....
}
Inbound Advanced Use Cases
Using Multiple Auth Handlers
The authHandlers
can be configured for advanced use cases, which use multiple auth handlers as follows:
Case 1: Auth should be successful for authHandler1
OR authHandler1
.
authHandlers: [authHandler1, authHandler2]
Case 2: Auth should be successful for authHandler1
AND authHandler12
.
authHandlers: [[authHandler1], [authHandler2]]
Case 3: Auth should be successful for ((authHandler1
OR authHandler2
) AND (authHandler3
OR authHandler4
)).
authHandlers: [[authHandler1, authHandler2], [authHandler3, authHandler4]]
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [authHandler1, authHandler2]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
service helloWorld on secureHelloWorldEp {
// ....
}
Using Multiple Scopes
The scopes
can be configured for advanced use cases as follows:
Case 1: Auth should be successful for scope-1
OR scope-2
.
scopes: ["scopes-1", "scopes-2"]
Case 2: Auth should be successful for scope-1
AND scope-2
.
scopes: [["scopes-1"], ["scopes-2"]]
Case 3: Auth should be successful for ((scope-1
OR scope-2
) AND (scope-3
OR scope-4
)).
scopes: [["scopes-1", "scopes-2"], ["scopes-3", "scopes-4"]]
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [authHandler],
scopes: ["scopes-1", "scopes-2"]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
service helloWorld on secureHelloWorldEp {
// ....
}
Per-Resource and Per-Service Customization
The security enforcements can be customized by the @http:ServiceConfig
annotation and the @http:ResourceConfig
annotation.
For example, authentication and authorization can be modified for a particular service as follows by configuring the auth
attribute of the @http:ServiceConfig
.
- Authentication can be disabled only for a particular service by using the
enabled
attribute - The authentication mechanism can be changed for a particular service by using the
authHandlers
attribute - Authorization scopes can be changed for a particular service by using the
scopes
attribute
@http:ServiceConfig {
basePath: "/hello",
auth: {
enabled: false,
authHandlers: [authHandlerA],
scopes: ["scope-A"]
}
}
service helloWorld on secureHelloWorldEp {
// ...
}
Further, authentication and authorization can be modified for a particular resource as follows by configuring the auth
attribute of the @http:ResourceConfig
:
@http:ResourceConfig {
basePath: "/",
auth: {
enabled: false,
authHandlers: [authHandlerA],
scopes: ["scope-A"]
}
}
resource function sayHello (http:Caller caller, http:Request req) {
// ...
}
The same configuration patterns used for the listener-level configurations are applied for authHandlers
and the scopes
attributes in service-level configurations and resource-level configurations.
Implementing Inbound Custom Authentication Mechanism
The user can implement a custom version of AuthHandler and AuthProvider with the use of the object-equivalency pattern as follows. With that, the http:Listener
can be enforced with custom authentication and authorization mechanisms.
public type InboundCustomAuthHandler object {
*http:InboundAuthHandler;
public function canProcess(http:Request req) returns @tainted boolean {
// Custom logic to check whether the request can be processed.
}
public function process(http:Request req) returns boolean|http:AuthenticationError {
// Custom logic to process the request, extract the credentials, and get them validated from the AuthProvider.
}
};
public type InboundCustomAuthProvider object {
*auth:InboundAuthProvider;
public function authenticate(string credential) returns boolean|auth:Error {
// Custom logic to authenticate the given credentials.
}
};
Disable HTTPS Enforcement
The enforcement of HTTPS can be disabled by configuring the value mandateSecureSocket
into false
as follows:
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [authHandler],
mandateSecureSocket: false
}
});
service helloWorld on secureHelloWorldEp {
// ....
}
Modify Authorization or Authentication Filter Index
The authn/authz filters are engaged as the top most filters of the filter array, which is configured in the HTTP listener configuration. The uer can configure the index of the authn/authz filters if it is needed to engage a custom filter before the authn/authz filters.
The position
attribute represents the authn/authz filter position of the filter array. The position values starts from 0 and it is set to 0 implicitly.
The following example engages the authn/authz filters in between the customFilter1
and customFilter2
. Then, the internally-updated filter chain would be [customFilter1, authnFilter, authzFilter, customFilter2]
.
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [authHandler],
position: 1
},
filters: [customFilter1, customFilter2],
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
service helloWorld on secureHelloWorldEp {
// ....
}
JWT Inbound Authentication and Authorization
Ballerina supports JWT Authentication and Authorization for services. The http:BearerAuthHandler
is used to extract the HTTP Authorization
header from the request and extract the credential from the header value which is Bearer <token>
. Then the extracted credential will be passed to the initialized AuthProvider and validated. The jwt:InboundJwtAuthProvider
is used to validate the credentials (JWT) passed by the AuthHandler against the jwt:JwtValidatorConfig
provided by the user.
JWT validation requires several additional configurations for the jwt:JwtValidatorConfig
including:
issuer
- The issuer of the JWTaudience
- The audience value for the current serviceclockSkewInSeconds
- Clock skew in seconds that can be used to avoid token validation failures due to clock synchronization problemstrustStoreConfig
- JWT trust store configurationstrustStore
- Trust store used for signature verificationcertificateAlias
- Token-signed public key certificate alias
jwtCache
- Cache used to store parsed JWT information asCachedJwt
The jwt:JwtValidatorConfig
record should be provided into the jwt:InboundJwtAuthProvider
when initializing. The initialized jwt:InboundJwtAuthProvider
is passed to the `http:BearerAuthHandler.
Note: For demonstration purposes, the
ballerinaTruststore.p12
included with Ballerina runtime is used. In a production deployment, the truststore should only contain the public key certificates of the trusted JWT issuers.
import ballerina/http;
import ballerina/jwt;
import ballerina/config;
jwt:InboundJwtAuthProvider jwtAuthProvider = new({
issuer: "ballerina",
audience: ["ballerina.io"],
trustStoreConfig: {
certificateAlias: "ballerina",
trustStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaTruststore.p12",
password: "ballerina"
}
}
});
http:BearerAuthHandler jwtAuthHandler = new(jwtAuthProvider);
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [jwtAuthHandler]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
@http:ServiceConfig {
basePath: "/hello"
}
service helloWorld on secureHelloWorldEp {
@http:ResourceConfig {
methods: ["GET"],
path: "/",
auth:{
scopes:["hello"],
enabled: true
}
}
resource function sayHello(http:Caller caller, http:Request req) {
http:Response resp = new;
resp.setTextPayload("Hello, World!");
checkpanic caller->respond(resp);
}
}
When the service is invoked without authentication information or invalid authentication information, an authentication failure will occur:
curl -k -v https://localhost:9091/hello
> GET /hello HTTP/1.1
> Host: localhost:9091
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< content-type: text/plain
<
Authentication failure
If a request is made with a valid, signed JWT but without the expected scope
, an authorization failure will occur. An example of a JWT without the scope
attribute is as follows.
{
"sub": "ballerina",
"iss": "ballerina",
"exp":<PHONE_NUMBER>,
"iat":<PHONE_NUMBER>,
"jti": "f5aded50585c46f2b8ca233d0c2a3c9d",
"aud": [
"ballerina",
"Ballerina.org",
"ballerina.io"
]
}
curl -k -v https://localhost:9091/hello -H "Authorization:Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiYWxsZXJpbmEiLCJpc3MiOiJiYWxsZXJpbmEiLCJleHAiOjI4MTg0MTUwMTksImlhdCI6MTUyNDU3NTAxOSwianRpIjoiZjVhZGVkNTA1ODVjNDZmMmI4Y2EyMzNkMGMyYTNjOWQiLCJhdWQiOlsiYmFsbGVyaW5hIiwiYmFsbGVyaW5hLm9yZyIsImJhbGxlcmluYS5pbyJdfQ.X2mHWCr8A5UaJFvjSPUammACnTzFsTdre-P5yWQgrwLBmfcpr9JaUuq4sEwp6to3xSKN7u9QKqRLuWH1SlcphDQn6kdF1ZrCgXRQ0HQTilZQU1hllZ4c7yMNtMgMIaPgEBrStLX1Ufr6LpDkTA4VeaPCSqstHt9WbRzIoPQ1fCxjvHBP17ShiGPRza9p_Z4t897s40aQMKbKLqLQ8rEaYAcsoRBXYyUhb_PRS-YZtIdo7iVmkMVFjYjHvmYbpYhNo57Z1Y5dNa8h8-4ON4CXzcJ1RzuyuFVz1a3YL3gWTsiliVmno7vKyRo8utirDRIPi0dPJPuWi2uMtJkqdkpzJQ"
> GET /hello HTTP/1.1
> Host: localhost:9091
> User-Agent: curl/7.47.0
> Accept: */*
> Authorization:Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiYWxsZXJ
pbmEiLCJpc3MiOiJiYWxsZXJpbmEiLCJleHAiOjI4MTg0MTUwMTksImlhdCI6MTUyNDU3NTAxOSwian
RpIjoiZjVhZGVkNTA1ODVjNDZmMmI4Y2EyMzNkMGMyYTNjOWQiLCJhdWQiOlsiYmFsbGVyaW5hIiwiY
mFsbGVyaW5hLm9yZyIsImJhbGxlcmluYS5pbyJdfQ.X2mHWCr8A5UaJFvjSPUammACnTzFsTdre-P5y
WQgrwLBmfcpr9JaUuq4sEwp6to3xSKN7u9QKqRLuWH1SlcphDQn6kdF1ZrCgXRQ0HQTilZQU1hllZ4c
7yMNtMgMIaPgEBrStLX1Ufr6LpDkTA4VeaPCSqstHt9WbRzIoPQ1fCxjvHBP17ShiGPRza9p_Z4t897
s40aQMKbKLqLQ8rEaYAcsoRBXYyUhb_PRS-YZtIdo7iVmkMVFjYjHvmYbpYhNo57Z1Y5dNa8h8-4ON4
CXzcJ1RzuyuFVz1a3YL3gWTsiliVmno7vKyRo8utirDRIPi0dPJPuWi2uMtJkqdkpzJQ
>
< HTTP/1.1 403 Forbidden
< content-type: text/plain
<
Authorization failure
A request with a correct “scope” attribute will result in a successful invocation. An example of a JWT that has the correct “scope” attribute is as follows.
{
"sub": "ballerina",
"iss": "ballerina",
"exp":<PHONE_NUMBER>,
"iat":<PHONE_NUMBER>,
"jti": "f5aded50585c46f2b8ca233d0c2a3c9d",
"aud": [
"ballerina",
"ballerina.org",
"ballerina.io"
],
"scope": "hello"
}
curl -k -v https://localhost:9091/hello -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiYWxsZXJpbmEiLCJpc3MiOiJiYWxsZXJpbmEiLCJleHAiOjI4MTg0MTUwMTksImlhdCI6MTUyNDU3NTAxOSwianRpIjoiZjVhZGVkNTA1ODVjNDZmMmI4Y2EyMzNkMGMyYTNjOWQiLCJhdWQiOlsiYmFsbGVyaW5hIiwiYmFsbGVyaW5hLm9yZyIsImJhbGxlcmluYS5pbyJdLCJzY29wZSI6ImhlbGxvIn0.bNoqz9_DzgeKSK6ru3DnKL7NiNbY32ksXPYrh6Jp0_O3ST7WfXMs9WVkx6Q2TiYukMAGrnMUFrJnrJvZwC3glAmRBrl4BYCbQ0c5mCbgM9qhhCjC1tBA50rjtLAtRW-JTRpCKS0B9_EmlVKfvXPKDLIpM5hnfhOin1R3lJCPspJ2ey_Ho6fDhsKE3DZgssvgPgI9PBItnkipQ3CqqXWhV-RFBkVBEGPDYXTUVGbXhdNOBSwKw5ZoVJrCUiNG5XD0K4sgN9udVTi3EMKNMnVQaq399k6RYPAy3vIhByS6QZtRjOG8X93WJw-9GLiHvcabuid80lnrs2-mAEcstgiHVw'
> GET /hello HTTP/1.1
> Host: localhost:9091
> User-Agent: curl/7.47.0
> Accept: */*
> Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiYWxsZX
JpbmEiLCJpc3MiOiJiYWxsZXJpbmEiLCJleHAiOjI4MTg0MTUwMTksImlhdCI6MTUyNDU3NTAxOSwia
nRpIjoiZjVhZGVkNTA1ODVjNDZmMmI4Y2EyMzNkMGMyYTNjOWQiLCJhdWQiOlsiYmFsbGVyaW5hIiwi
YmFsbGVyaW5hLm9yZyIsImJhbGxlcmluYS5pbyJdLCJzY29wZSI6ImhlbGxvIn0.bNoqz9_DzgeKSK6
ru3DnKL7NiNbY32ksXPYrh6Jp0_O3ST7WfXMs9WVkx6Q2TiYukMAGrnMUFrJnrJvZwC3glAmRBrl4BY
CbQ0c5mCbgM9qhhCjC1tBA50rjtLAtRW-JTRpCKS0B9_EmlVKfvXPKDLIpM5hnfhOin1R3lJCPspJ2e
y_Ho6fDhsKE3DZgssvgPgI9PBItnkipQ3CqqXWhV-RFBkVBEGPDYXTUVGbXhdNOBSwKw5ZoVJrCUiNG
5XD0K4sgN9udVTi3EMKNMnVQaq399k6RYPAy3vIhByS6QZtRjOG8X93WJw-9GLiHvcabuid80lnrs2-
mAEcstgiHVw
>
< HTTP/1.1 200 OK
< content-type: text/plain
<
Hello, World!
OAuth2 Inbound Authentication and Authorization
Ballerina supports OAuth2 Authentication and Authorization for services. The http:BearerAuthHandler
is used to extract the HTTP Authorization
header from the request and extract the credentials from the header value, which is the Bearer <token>
. Then, the extracted credentials will be passed to the initialized AuthProvider to get them validated. The oauth2:InboundOAuth2Provider
is used to validate the credentials passed by the AuthHandler against the introspection endpoint configured at oauth2:IntrospectionServerConfig
, which is provided by the user.
OAuth2 token validation requires several additional configurations for the oauth2:IntrospectionServerConfig
including:
url
- URL of the introspection servertokenTypeHint
- A hint about the type of the token submitted for introspectionclientConfig
- HTTP client configurations, which calls the introspection server
The oauth2:IntrospectionServerConfig
record should be provided into the oauth2:InboundOAuth2Provider
when initializing and the initialized oauth2:InboundOAuth2Provider
is passed to the http:BearerAuthHandler
.
import ballerina/http;
import ballerina/oauth2;
import ballerina/config;
oauth2:InboundOAuth2Provider oauth2Provider = new({
url: "https://localhost:9196/oauth2/token/introspect",
tokenTypeHint: "access_token"
});
http:BearerAuthHandler oauth2Handler = new(oauth2Provider);
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [oauth2Handler]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
@http:ServiceConfig {
basePath: "/hello"
}
service helloWorld on secureHelloWorldEp {
@http:ResourceConfig {
methods: ["GET"],
path: "/"
}
resource function sayHello(http:Caller caller, http:Request req) {
http:Response resp = new;
resp.setTextPayload("Hello, World!");
checkpanic caller->respond(resp);
}
}
When the service is invoked without authentication information or invalid authentication information, an authentication failure will occur:
curl -k -v https://localhost:9091/hello
> GET /hello HTTP/1.1
> Host: localhost:9091
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< content-type: text/plain
<
Authentication failure
Although a request is made with valid authentication information, if the introspection endpoint does not respond either with the scope
attribute of the response JSON payload or with an unexpected scope
attribute, an authorization failure will occur.
curl -k -v https://localhost:9091/hello -H "Authorization:Bearer <token>"
> GET /hello HTTP/1.1
> Host: localhost:9091
> User-Agent: curl/7.47.0
> Accept: */*
> Authorization:Bearer <token>
>
< HTTP/1.1 403 Forbidden
< content-type: text/plain
<
Authorization failure
A request, which gets a successful response from the introspection endpoint with a correct “scope” attribute will result in a successful invocation.
curl -k -v https://localhost:9091/hello -H 'Authorization: Bearer <token>'
> GET /hello HTTP/1.1
> Host: localhost:9091
> User-Agent: curl/7.47.0
> Accept: */*
> Authorization: Bearer <token>
>
< HTTP/1.1 200 OK
< content-type: text/plain
<
Hello, World!
LDAP Inbound Authentication and Authorization
Ballerina supports LDAP Authentication and Authorization for services. The http:BasicAuthHandler
is used to extract the HTTP Authorization
header from the request and extract the credentials from the header value, which is Basic <token>
. Then, the extracted credentials will be passed to the initialized AuthProvider to get validated. The ldap:InboundLdapAuthProvider
is used to validate the credentials passed by the AuthHandler against the LDAP server configured at ldap:LdapConnectionConfig
, which is provided by the user.
LDAP token validation requires several additional configurations for the ldap:LdapConnectionConfig
including:
domainName
- Unique name to identify the user storeconnectionURL
- Connection URL to the LDAP serverconnectionName
- The username to connect to the LDAP serverconnectionPassword
- Password for the ConnectionName useruserSearchBase
- DN of the context or object under which the user entries are stored in the LDAP serveruserEntryObjectClass
- Object class used to construct user entriesuserNameAttribute
- The attribute used for uniquely identifying a user entryuserNameSearchFilter
- Filtering criteria used to search for a particular user entryuserNameListFilter
- Filtering criteria for searching user entries in the LDAP servergroupSearchBase
- DN of the context or object under which the group entries are stored in the LDAP servergroupEntryObjectClass
- Object class used to construct group entriesgroupNameAttribute
- The attribute used for uniquely identifying a group entrygroupNameSearchFilter
- Filtering criteria used to search for a particular group entrygroupNameListFilter
- Filtering criteria for searching group entries in the LDAP servermembershipAttribute
- Define the attribute that contains the distinguished names (DN) of user objects that are in a groupuserRolesCacheEnabled
- To indicate whether to cache the role list of a userconnectionPoolingEnabled
- Define whether LDAP connection pooling is enabledconnectionTimeoutInMillis
- Timeout in making the initial LDAP connectionreadTimeoutInMillis
- Read timeout in milliseconds for LDAP operationsretryAttempts
- Retry the authentication request if a timeout happenedsecureClientSocket
- The SSL configurations for the LDAP client socket. This needs to be configured in order to communicate through LDAPs
The ldap:LdapConnectionConfig
record should be provided into the ldap:InboundLdapAuthProvider
when initializing and the initialized ldap:InboundLdapAuthProvider
is passed to the http:BasicAuthHandler
.
import ballerina/http;
import ballerina/ldap;
import ballerina/config;
ldap:LdapConnectionConfig ldapConfig = {
domainName: "ballerina.io",
connectionURL: "ldap://localhost:20100",
connectionName: "uid=admin,ou=system",
connectionPassword: "secret",
userSearchBase: "ou=Users,dc=ballerina,dc=io",
userEntryObjectClass: "identityPerson",
userNameAttribute: "uid",
userNameSearchFilter: "(&(objectClass=person)(uid=?))",
userNameListFilter: "(objectClass=person)",
groupSearchBase: ["ou=Groups,dc=ballerina,dc=io"],
groupEntryObjectClass: "groupOfNames",
groupNameAttribute: "cn",
groupNameSearchFilter: "(&(objectClass=groupOfNames)(cn=?))",
groupNameListFilter: "(objectClass=groupOfNames)",
membershipAttribute: "member",
userRolesCacheEnabled: true,
connectionPoolingEnabled: false,
connectionTimeoutInMillis: 5000,
readTimeoutInMillis: 60000,
retryAttempts: 3
};
ldap:InboundLdapAuthProvider ldapAuthProvider = new(ldapConfig, "ldap01");
http:BasicAuthHandler ldapAuthHandler = new(ldapAuthProvider);
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [ldapAuthHandler]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
@http:ServiceConfig {
basePath: "/hello",
auth: {
scopes: ["hello"]
}
}
service helloWorld on secureHelloWorldEp {
@http:ResourceConfig {
methods: ["GET"],
path: "/"
}
resource function sayHello(http:Caller caller, http:Request req) {
http:Response resp = new;
resp.setTextPayload("Hello, World!");
checkpanic caller->respond(resp);
}
}
When the service is invoked without authentication information or invalid authentication information, an authentication failure will occur:
curl -k -v https://localhost:9091/hello
> GET /hello HTTP/1.1
> Host: localhost:9091
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< content-type: text/plain
<
Authentication failure
If a request is made with valid authentication information but the LDAP server responds with an empty group list or unexpected scopes, an authorization failure will occur.
curl -k -v https://localhost:9091/hello -H "Authorization: Basic <token>"
> GET /hello HTTP/1.1
> Host: localhost:9091
> User-Agent: curl/7.47.0
> Accept: */*
> Authorization:Bearer <token>
>
< HTTP/1.1 403 Forbidden
< content-type: text/plain
<
Authorization failure
A request, which gets a successful response from the LDAP server for the “scope” request will result in a successful invocation.
curl -k -v https://localhost:9091/hello -H 'Authorization: Basic <token>'
> GET /hello HTTP/1.1
> Host: localhost:9091
> User-Agent: curl/7.47.0
> Accept: */*
> Authorization: Bearer <token>
>
< HTTP/1.1 200 OK
< content-type: text/plain
<
Hello, World!
Basic Auth Inbound Authentication and Authorization
Ballerina supports Basic Authentication and Authorization for services. The http:BasicAuthHandler
is used to extract the HTTP Authorization
header from the request and extract the credential from the header value, which is the Basic <token>
. Then, the extracted credentials will be passed to the initialized AuthProvider and gets validated. The jwt:InboundBasicAuthProvider
is used to read the user information from the configuration file and authenticate the credentials passed by the AuthHandler.
import ballerina/auth;
import ballerina/http;
import ballerina/config;
auth:InboundBasicAuthProvider basicAuthProvider = new;
http:BasicAuthHandler basicAuthHandler = new(basicAuthProvider);
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [basicAuthHandler]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
@http:ServiceConfig {
basePath: "/hello",
auth: {
scopes: ["hello"]
}
}
service helloWorld on secureHelloWorldEp {
@http:ResourceConfig {
methods: ["GET"],
path: "/"
}
resource function sayHello(http:Caller caller, http:Request req) {
http:Response resp = new;
resp.setTextPayload("Hello, World!");
checkpanic caller->respond(resp);
}
}
To enforce Basic Authentication, users and scopes should be configured through a configuration file. The following example file introduces two users. The generalUser
has no scopes and the admin
user has the hello
scope.
sample-users.toml
[b7a.users]
[b7a.users.generalUser]
password="@encrypted:{pIQrB9YfCQK1eIWH5d6UaZXA3zr+60JxSBcpa2PY7a8=}"
[b7a.users.admin]
password="@encrypted:{pIQrB9YfCQK1eIWH5d6UaZXA3zr+60JxSBcpa2PY7a8=}"
scopes="hello"
Restart the service using the following command.
ballerina run --config sample-users.toml basic_auth_sample.bal
Since passwords are encrypted, the Config API will request the decryption key. Use ballerina
as the decryption key in this sample.
Also, the passwords can be hashed and provided with the configuration file. The following example file introduces three users along with the passwords hashed with sha256
, sha384
, and sha512
hashing algorithms.
sample-users.toml
[b7a.users]
[b7a.users.userA]
password="@sha256:{cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf6860}"
[b7a.users.userB]
password="@sha384:{1249e15f035ed34786a328d9fdb2689ab24f7c7b253d1b7f66ed92a679d663dd502d7beda59973e8c91a728b929fc8cd}"
[b7a.users.userC]
password="@sha512:{9057ff1aa9509b2a0af624d687461d2bbeb07e2f37d953b1ce4a9dc921a7f19c45dc35d7c5363b373792add57d0d7dc41596e1c585d6ef7844cdf8ae87af443f}"
Once the service is restarted with the first configuration file in place, the generalUser
will not be able to invoke the service due to authorization failure:
curl -k -v -u generalUser:password https://localhost:9091/hello
> GET /hello HTTP/1.1
> Host: localhost:9091
> User-Agent: curl/7.47.0
> Accept: */*
< HTTP/1.1 403 Forbidden
< content-type: text/plain
<
Authorization failure
‘Admin’ users will be able to invoke the service:
curl -k -v -u admin:password https://localhost:9091/hello
> GET /hello HTTP/1.1
> Host: localhost:9091
> User-Agent: curl/7.47.0
> Accept: */*
< HTTP/1.1 200 OK
< content-type: text/plain
<
Hello, World!
Outbound Authentication and Authorization
The Ballerina HTTP client can be configured to send authentication and authorization information to the endpoint being invoked. Ballerina has built-in support for the following outbound authentication mechanisms, whereas it is possible to add custom mechanisms:
- Basic authentication
- JWT authentication
- OAuth2 authentication
Ballerina outbound authentication is also abstracted out into 2 layers called http:OutboundAuthHandler
and auth:OutboundAuthProvider
.
The auth:OutboundAuthProvider
is a protocol-independent entity, which only knows how to generate credentials with the necessary information provided by the user. The http:OutboundAuthHandler
can be protocol dependent. Even-though the current focus is on HTTP, the ballerina/auth
module can operate with other protocols as well.
The auth:OutboundAuthProvider
is used to create the credentials according to the provided configurations. The http:OutboundAuthHandler
is used to get the created credentials from the auth:OutboundAuthProvider
and perform HTTP-level actions, which are adding the required HTTP headers or body using the received credentials.
In a particular authentication scheme, the implemented instance of the auth:OutboundAuthProvider
is initialized with required configurations and it is passed to the implemented instance of the http:OutboundAuthHandler
.
Next, the implemented instance of the http:OutboundAuthHandler
is passed to the http:Client
configuration as follows, and the client is initialized with authentication.
The following example represents how a client is secured with Basic Auth with the above-mentioned configurations.
import ballerina/auth;
import ballerina/http;
import ballerina/config;
auth:OutboundBasicProvider basicAuthProvider = new({
username: "user",
password: "ballerina"
});
http:BasicAuthHandler basicAuthHandler = new(basicAuthProvider);
http:Client secureHelloWorldClient = new("https://localhost:9092", {
auth: {
authHandler: basicAuthHandler
},
secureSocket: {
trustStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaTruststore.p12",
password: "ballerina"
}
}
});
Note: It is better to use HTTPS when enforcing authentication and authorization checks to ensure the confidentiality of sensitive authentication data.
Outbound Advanced Use Cases
Implementing Outbound Custom Authentication Mechanism
The user can implement a custom version of the AuthHandler and AuthProvider with the use of the object equivalency pattern as follows. With that, the http:Client
can be enforced with custom authentication and authorization mechanisms.
public type OutboundCustomAuthHandler object {
*http:OutboundAuthHandler;
public function prepare(http:Request req) returns http:Request|http:AuthenticationError {
// Custom logic to prepare the request.
}
public function inspect(http:Request req, http:Response resp) returns http:Request|http:AuthenticationError? {
// Custom logic to inspect the request after the initial outbound call.
}
};
public type OutboundCustomAuthProvider object {
*auth:OutboundAuthProvider;
public function generateToken() returns string|auth:Error {
// Custom logic to generate the token.
}
public function inspect(map<anydata> data) returns string|auth:Error? {
// Custom logic to inspect the data map received from the AuthHandler.
}
};
JWT Outbound Authentication
Ballerina supports JWT Authentication for clients. The jwt:OutboundJwtAuthProvider
is used to issue a JWT against the jwt:JwtIssuerConfig
provided by the user. The http:BearerAuthHandler
is used to add the HTTP Authorization
header with the value received from the AuthProvider as the Bearer <token>
.
JWT issuing requires several additional configurations for the jwt:JwtIssuerConfig
including:
username
- JWT token usernameissuer
- JWT token issueraudience
- JWT token audiencecustomClaims
- Map of custom claimsexpTime
- JWT token expiry timekeyStoreConfig
- JWT key store configurationskeyStore
- Keystore to be used in JWT signingkeyAlias
- Signing key aliaskeyPassword
- Signing key password
signingAlg
- JWT signing algorithmjwt:RS256
- The RSA-SHA256 algorithmjwt:RS384
- The RSA-SHA384 algorithmjwt:RS512
- The RSA-SHA512 algorithmjwt:NONE
- Unsecured JWTs (no signing)
Thejwt:JwtIssuerConfig
record should be provided into the jwt:OutboundJwtAuthProvider
when initializing and the initialized jwt:OutboundJwtAuthProvider
is passed to the http:BearerAuthHandler
.
import ballerina/http;
import ballerina/jwt;
import ballerina/config;
jwt:OutboundJwtAuthProvider jwtAuthProvider = new({
username: "ballerinaUser",
issuer: "ballerina",
audience: ["ballerina.io"],
keyStoreConfig: {
keyAlias: "ballerina",
keyPassword: "ballerina",
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
http:BearerAuthHandler jwtAuthHandler = new(jwtAuthProvider);
http:Client downstreamServiceEP = new("https://localhost:9091", {
auth: {
authHandler: jwtAuthHandler
},
secureSocket: {
trustStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaTruststore.p12",
password: "ballerina"
}
}
});
The http:Client
defined in the program calls the the http:Listener
, which is secured with JWT authentication (For more information, see the example added under JWT inbound authentication).
OAuth2 Outbound Authentication
Ballerina supports OAuth2 Authentication for clients. It supports the Client Credentials grant type, Password grant type, and Direct Token mode, in which, the credentials can be provided manually and after that refreshing is handled internally.
The oauth2:OutboundOAuth2Provider
is used to create a token against the configuration provided by the user. It can be the oauth2:ClientCredentialsGrantConfig
, oauth2:PasswordGrantConfig
, or oauth2:DirectTokenConfig
according to the grant type that is required by the user. The http:BearerAuthHandler
is used to add the HTTP Authorization
header with the value received from the AuthProvider as the Bearer <token>
.
Client Credentials Grant Type
OAuth2 token issuing requires several additional configurations for the oauth2:ClientCredentialsGrantConfig
including:
tokenUrl
- Token URL for the authorization endpointclientId
- Client ID for the client credentials grant authenticationclientSecret
- Client secret for the client credentials grant authenticationscopes
- Scope of the access requestclockSkewInSeconds
- Clock skew in secondsretryRequest
- Retry the request if the initial request returns a 401 responsecredentialBearer
- How authentication credentials are sent to the authorization endpointhttp:AUTH_HEADER_BEARER
- Indicates that the authentication credentials should be sent via the Authentication Headerhttp:POST_BODY_BEARER | NO_BEARER
- Indicates that the Authentication credentials should be sent via the body of the POST request
clientConfig
- HTTP client configurations,which calls the authorization endpoint
The oauth2:ClientCredentialsGrantConfig
record should be provided into the oauth2:OutboundOAuth2Provider
when initializing and the initialized oauth2:OutboundOAuth2Provider
is passed to the http:BearerAuthHandler
.
import ballerina/http;
import ballerina/oauth2;
import ballerina/config;
oauth2:OutboundOAuth2Provider oauth2Provider = new({
tokenUrl: "https://localhost:9196/oauth2/token/authorize",
clientId: "3MVG9YDQS5WtC11paU2WcQjBB3L5w4gz52uriT8ksZ3nUVjKvrfQMrU4uvZohTftxStwNEW4cfStBEGRxRL68",
clientSecret: "9205371918321623741",
scopes: ["token-scope1", "token-scope2"]
});
http:BearerAuthHandler oauth2AuthHandler = new(oauth2Provider);
http:Client downstreamServiceEP = new("https://localhost:9091", {
auth: {
authHandler: oauth2AuthHandler
},
secureSocket: {
trustStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaTruststore.p12",
password: "ballerina"
}
}
});
Password Grant Type
OAuth2 token issuing requires several additional configurations for the oauth2:PasswordGrantConfig
including:
tokenUrl
- Token URL for the authorization endpointusername
- Username for password grant authenticationpassword
- Password for password grant authenticationclientId
- Client ID for password grant authenticationclientSecret
- Client secret for password grant authenticationscopes
- Scope of the access requestrefreshConfig
- Configurations for refreshing the access tokenrefreshUrl
- Refresh token URL for the refresh token serverscopes
- Scope of the access requestcredentialBearer
- How authentication credentials are sent to the authorization endpointclientConfig
- HTTP client configurations, which calls the authorization endpoint
clockSkewInSeconds
- Clock skew in secondsretryRequest
- Retry the request if the initial request returns a 401 responsecredentialBearer
- How authentication credentials are sent to the authorization endpointhttp:AUTH_HEADER_BEARER
- Indicates that the authentication credentials should be sent via the Authentication Headerhttp:POST_BODY_BEARER|NO_BEARER
- Indicates that the Authentication credentials should be sent via the body of the POST request
clientConfig
- HTTP client configurations, which calls the authorization endpoint
The oauth2:PasswordGrantConfig
record should be provided into the oauth2:OutboundOAuth2Provider
when initializing and the initialized oauth2:OutboundOAuth2Provider
is passed to the http:BearerAuthHandler
.
import ballerina/http;
import ballerina/oauth2;
import ballerina/config;
oauth2:OutboundOAuth2Provider oauth2Provider = new({
tokenUrl: "https://localhost:9196/oauth2/token/authorize",
username: "johndoe",
password: "A3ddj3w",
clientId: "3MVG9YDQS5WtC11paU2WcQjBB3L5w4gz52uriT8ksZ3nUVjKvrfQMrU4uvZohTftxStwNEW4cfStBEGRxRL68",
clientSecret: "9205371918321623741",
scopes: ["token-scope1", "token-scope2"],
refreshConfig: {
refreshUrl: "https://localhost:9196/oauth2/token/refresh",
scopes: ["token-scope1", "token-scope2"]
}
});
http:BearerAuthHandler oauth2AuthHandler = new(oauth2Provider);
http:Client downstreamServiceEP = new("https://localhost:9091", {
auth: {
authHandler: oauth2AuthHandler
},
secureSocket: {
trustStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaTruststore.p12",
password: "ballerina"
}
}
});
Direct Token Mode
OAuth2 token issuing requires several additional configurations for the oauth2:DirectTokenConfig
including:
accessToken
- Access token for the authorization endpointrefreshConfig
- Configurations for refreshing the access tokenrefreshUrl
- Refresh token URL for the refresh token serverrefreshToken
- Refresh token for the refresh token serverclientId
- Client ID for authentication with the authorization endpointclientSecret
- Client secret for authentication with the authorization endpointscopes
- Scope of the access requestcredentialBearer
- How authentication credentials are sent to the authorization endpointclientConfig
- HTTP client configurations, which calls the authorization endpoint
clockSkewInSeconds
- Clock skew in secondsretryRequest
- Retry the request if the initial request returns a 401 responsecredentialBearer
- How authentication credentials are sent to the authorization endpointhttp:AUTH_HEADER_BEARER
- Indicates that the authentication credentials should be sent via the Authentication Headerhttp:POST_BODY_BEARER|NO_BEARER
- Indicates that the Authentication credentials should be sent via the body of the POST request
The oauth2:DirectTokenConfig
record should be provided into the oauth2:OutboundOAuth2Provider
when initializing and the initialized oauth2:OutboundOAuth2Provider
is passed to the http:BearerAuthHandler
.
import ballerina/http;
import ballerina/oauth2;
oauth2:OutboundOAuth2Provider oauth2Provider = new({
accessToken: "34060588-dd4e-36a5-ad93-440cc77a1cfb",
refreshConfig: {
refreshToken: "15160398-ae07-71b1-aea1-411ece712e59",
refreshUrl: "https://ballerina.io/sample/refresh",
clientId: "rgfKVdnMQnJSSr_pKFTxj3apiwYa",
clientSecret: "BRebJ0aqfclQB9v7yZwhj0JfW0ga"
}
});
http:BearerAuthHandler oauth2AuthHandler = new(oauth2Provider);
http:Client downstreamServiceEP = new("https://localhost:9091", {
auth: {
authHandler: oauth2AuthHandler
},
secureSocket: {
trustStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaTruststore.p12",
password: "ballerina"
}
}
});
Basic Auth Outbound Authentication
Ballerina supports Basic Authentication for clients. The auth:OutboundBasicAuthProvider
is used to create a token against the auth:Credential
provided by the user. The http:BasicAuthHandler
is used to add the HTTP Authorization
header with the value received from the AuthProvider as the Basic <token>
.
Token issuing requires several additional configurations for the auth:Credential
config including:
username
- The username for Basic authenticationpassword
- The password for Basic authentication
The auth:Credential
record should be provided into the auth:OutboundBasicAuthProvider
when initializing and the initialized auth:OutboundBasicAuthProvider
is passed to the http:BasicAuthHandler
.
import ballerina/auth;
import ballerina/http;
import ballerina/config;
auth:OutboundBasicProvider basicAuthProvider = new({
username: "user",
password: "ballerina"
});
http:BasicAuthHandler basicAuthHandler = new(basicAuthProvider);
http:Client downstreamServiceEP = new("https://localhost:9091", {
auth: {
authHandler: basicAuthHandler
},
secureSocket: {
trustStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaTruststore.p12",
password: "ballerina"
}
}
});
Token Propagation for Outbound Authentication
Ballerina supports token propagation for outbound authentication. The token propagation happens if the user does not provide any configuration when initializing the auth:OutboundAuthProvider
.
The auth:OutboundAuthProvider
reads the token/username from the runtime:InvocationContext
according to the outbound authentication scheme and uses that for the outbound request. The runtime:InvocationContext
is initialized based on the authentication information from the inbound request.
Example One
The following program has an http:Client
secured with Basic authentication and it is configured inside an http:Listener
secured with Basic authentication.
The auth:OutboundBasicAuthProvider
is initialized without providing any configurations. Therefore, the program gets the token from the runtime:InvocationContext
and uses it for the outbound request.
If the downstream service is also secured with Basic authentication and as same as the upstream service, the user does not need to configure the client.
Note: This scenario is the same for all the scenarios in which both the upstream and downstream services are secured using the same authentication scheme and clients are also configured using the same authentication scheme but without any configurations. The token propagation happens internally.
import ballerina/auth;
import ballerina/http;
import ballerina/config;
auth:InboundBasicAuthProvider inboundBasicAuthProvider = new;
http:BasicAuthHandler inboundBasicAuthHandler = new(inboundBasicAuthProvider);
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [inboundBasicAuthHandler]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
auth:OutboundBasicAuthProvider outboundBasicAuthProvider = new;
http:BasicAuthHandler outboundBasicAuthHandler = new(outboundBasicAuthProvider);
http:Client downstreamClientEP = new("https://localhost:9092", {
auth: {
authHandler: outboundBasicAuthHandler
},
secureSocket: {
trustStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaTruststore.p12",
password: "ballerina"
}
}
});
@http:ServiceConfig {
basePath: "/hello",
auth: {
scopes: ["hello"]
}
}
service helloWorld on secureHelloWorldEp {
@http:ResourceConfig {
methods: ["GET"],
path: "/"
}
resource function sayHello(http:Caller caller, http:Request req) returns error? {
// http:Request req = new;
http:Response response = check downstreamClientEP->get("/downstream");
checkpanic caller->respond(response);
}
}
// ----------------------------------------------
// Following code creates the downstream service
// ----------------------------------------------
listener http:Listener downstreamServiceEp = new(9092, {
auth: {
authHandlers: [inboundBasicAuthHandler]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
@http:ServiceConfig {
basePath: "/downstream"
}
service downStreamService on downstreamServiceEp {
@http:ResourceConfig {
methods: ["GET"],
path: "/"
}
resource function downStreamResource(http:Caller caller, http:Request req) {
http:Response resp = new;
resp.setTextPayload("Downstream service received authenticated request with the token: " + req.getHeader("Authorization"));
checkpanic caller->respond(resp);
}
}
To enforce Basic Authentication, create a configuration file as follows:
sample-users.toml
[b7a.users]
[b7a.users.tom]
password="123"
scopes="hello"
Start the service using the following command after creating the sample-users.toml
file.
ballerina run --config sample-users.toml example.bal
The Tom
user will be able to invoke the /hello
resource and invoke the Basic Auth protected downstream service.
curl -k -v -u tom:123 https://localhost:9091/hello
> GET /hello HTTP/1.1
> Host: localhost:9091
> Authorization: Basic dG9tOjEyMw==
> User-Agent: curl/7.60.0
> Accept: */*
< HTTP/1.1 200 OK
< content-type: text/plain
< content-length: 602
<
Downstream service received authenticated request with the token: Basic dG9tOjEyMw==
Example Two
The following program has an http:Client
secured with JWT authentication and it is configured inside an http:Listener
secured with Basic Authentication.
The jwt:OutboundJwtAuthProvider
is initialized using the provides configurations but without the username. Therefore, the program gets the username from the runtime:InvocationContext
, which is set based on the inbound authentication information and uses it for the outbound request.
In this example, the downstream service is secured using JWT authentication and expects a JWT issued against the user authenticating by the upstream service (protected by Basic authentication). Ballerina can dynamically issue such JWT while propagating the user information internally.
import ballerina/auth;
import ballerina/http;
import ballerina/jwt;
import ballerina/config;
auth:InboundBasicAuthProvider inboundBasicAuthProvider = new;
http:BasicAuthHandler inboundBasicAuthHandler = new(inboundBasicAuthProvider);
listener http:Listener secureHelloWorldEp = new(9091, {
auth: {
authHandlers: [inboundBasicAuthHandler]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
jwt:OutboundJwtAuthProvider outboundJwtAuthProvider = new({
issuer: "ballerina",
audience: ["ballerina.io"],
keyStoreConfig: {
keyAlias: "ballerina",
keyPassword: "ballerina",
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
http:BearerAuthHandler outboundJwtAuthHandler = new(outboundJwtAuthProvider);
http:Client downstreamClientEP = new("https://localhost:9092", {
auth: {
authHandler: outboundJwtAuthHandler
},
secureSocket: {
trustStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaTruststore.p12",
password: "ballerina"
}
}
});
@http:ServiceConfig {
basePath: "/hello",
auth: {
scopes: ["hello"]
}
}
service helloWorld on secureHelloWorldEp {
@http:ResourceConfig {
methods: ["GET"],
path: "/"
}
resource function sayHello(http:Caller caller, http:Request req) returns error? {
// http:Request req = new;
http:Response response = check downstreamClientEP->get("/downstream");
checkpanic caller->respond(response);
}
}
// ----------------------------------------------
// Following code creates the downstream service
// ----------------------------------------------
jwt:InboundJwtAuthProvider inboundJwtAuthProvider = new({
issuer: "ballerina",
audience: ["ballerina.io"],
certificateAlias: "ballerina",
trustStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaTruststore.p12",
password: "ballerina"
}
});
http:BearerAuthHandler inboundJwtAuthHandler = new(inboundJwtAuthProvider);
listener http:Listener downstreamServiceEp = new(9092, {
auth: {
authHandlers: [inboundJwtAuthHandler]
},
secureSocket: {
keyStore: {
path: config:getAsString("b7a.home") + "/bre/security/ballerinaKeystore.p12",
password: "ballerina"
}
}
});
@http:ServiceConfig {
basePath: "/downstream"
}
service downStreamService on downstreamServiceEp {
@http:ResourceConfig {
methods: ["GET"],
path: "/"
}
resource function downStreamResource(http:Caller caller, http:Request req) {
http:Response resp = new;
resp.setTextPayload("Downstream service received authenticated request with the token: " + req.getHeader("Authorization"));
checkpanic caller->respond(resp);
}
}
To enforce Basic Authentication, create a configuration file as follows:
sample-users.toml
[b7a.users]
[b7a.users.tom]
password="123"
scopes="hello"
Start the service using the following command after creating the sample-users.toml
file.
ballerina run --config sample-users.toml example.bal
The ‘Tom’ user will be able to invoke the /hello
resource and invoke the Basic Auth protected downstream service.
curl -k -v -u tom:123 https://localhost:9091/hello
> GET /hello HTTP/1.1
> Host: localhost:9091
> Authorization: Basic dG9tOjEyMw==
> User-Agent: curl/7.60.0
> Accept: */*
< HTTP/1.1 200 OK
< content-type: text/plain
< content-length: 602
<
Downstream service received authenticated request with the token: Bearer eyJhbGciOiJSUzI1NiIsICJ0eXAiOiJKV1QifQ==.eyJzdWIiOiJ0b20iLCAiaXNzIjoiYmFsbGVyaW5hIiwgImV4cCI6MTU2NTUwMzUzNywgImlhdCI6MTU2NTUwMzIzNywgImp0aSI6ImJhMjczNTM5LTcxZWItNDExOC04MzNiLTQyNDlhMjY0MmZmNCIsICJhdWQiOlsiYmFsbGVyaW5hLmlvIl19.OwMHPrQfjpIujHSAIq3ycKsP4SYTR2nW9lBHXBOgIZV6-FcM1Lxz8xtjY1AwcIAd_L4XmiODN_5HYUluZb3jDj1F6ZeI4FQeTKygiGgJs_nTww56bHFQXPe9_IW1zxRGM8G51cJBKxTH6YsOTXgNVhcGoe5f-kaESmGze-XLCnCXgj0GYnG2ECnejSHRh89gjWMyfyMFRDhioPi9IYZEQGIFBQzrModFWXNKQZh5vxaF5KW4KWXLTRgrBX8uY2IIS6S80nf83oaUlrrApieaGf88cwSqOjGjaPpxj3I810qGa996ZVE3P5DkzgxrJYHrJMwcVgBX7sgDFUUcD3RrEA==


This module filters the variants between two populations defined in the PED file. Eg:The user could filter variants that are shared by specific Gender in Population1 present at a user specified minimum minor allele frequency. These variants are further compared to the Population2 i.e. filters for those varaints which are present at a user specified maximum minor allele frequency in specific gender in Population2
Select Population1: Population names to be chosen as Population1. Multiple population names can be selected.
Chose Gender: Enter the gender of the population to be queried.
Minimum MAF: Enter the minimum minor allele frequency of the variants in population1 to be retrieved.
Select Population2: Population names to be chosen as Population2. Multiple population names can be selected.
Chose Gender: Enter the gender of the population to be queried.
Maximum MAF: Enter the maximum minor allele frequency of the variants in population2.
Required columns in PED file:IndividualID, Population and Gender fields are mandatory for population module while other optional fields can be present. Gender could be coded as male/female or 1 for males and 2 for females and NA if unavailable.