Cookie - ALTERNATIVE
Kerberos - ALTERNATIVE
Forms Subflow - ALTERNATIVE
Username/Password Form - REQUIRED
OTP Password Form - OPTIONAL
Authentication SPI
Keycloak comes out of the box with a bunch of different authentication mechanisms: kerberos, password, and otp. These mechanisms may not meet all of your requirements and you may want to plug in your own custom ones. Keycloak provides an authentication SPI that you can use to write new plugins. The admin console supports applying, ordering, and configuring these new mechanisms.
Keycloak also supports a simple registration form. Different aspects of this form can be enabled and disabled i.e. Recaptcha support can be turned off and on. The same authentication SPI can be used to add another page to the registration flow or reimplement it entirely. There’s also an additional fine-grain SPI you can use to add specific validations and user extensions to the built in registration form.
A required action in Keycloak is an action that a user has to perform after he authenticates. After the action is performed successfully, the user doesn’t have to perform the action again. Keycloak comes with some built in required actions like "reset password". This action forces the user to change their password after they have logged in. You can write and plug in your own required actions.
Terms
To first learn about the Authentication SPI, let’s go over some of the terms used to describe it.
- Authentication Flow
-
A flow is a container for all authentications that must happen during login or registration.  Flows can contain other flows. You can also bind a new different flow for browser login, direct grant access, and registration.
- Authenticator
-
An authenticator is a pluggable component that hold the logic for performing the authentication or action within a flow. It is usually a singleton.
- Execution
-
An execution is an object that binds the authenticator to the flow and the authenticator to the configuration of the authenticator. Flows contain execution entries.
- Execution Requirement
-
Each execution defines how an authenticator behaves in a flow. The requirement defines whether the authenticator is enabled, disabled, optional, required, or an alternative. An alternative requirement means that the authentiactor is optional unless no other alternative authenticator is successful in the flow. For example, cookie authentication, kerberos, and the set of all login forms are all alternative. If one of those is successful, none of the others are executed.
- Authenticator Config
-
This object defines the configuration for the Authenticator for a specific execution within an authentication flow. Each execution can have a different config.
- Required Action
-
After authentication completes, the user might have one or more one-time actions he must complete before he is allowed to login. The user might be required to set up an OTP token generator or reset an expired password or even accept a Terms and Conditions document.
Algorithm Overview
Let’s talk about how this all works for browser login. Let’s assume the following flows, executions and sub flows.
In the top level of the form we have 3 executions of which all are alternatively required. This means that if any of these are successful, then the others do not have to execute. The Username/Password form is not executed if there is an SSO Cookie set or a successful Kerberos login. Let’s walk through the steps from when a client first redirects to keycloak to authenticate the user.
-
The OpenID Connect or SAML protocol provider unpacks relevent data, verifies the client and any signatures. It creates a AuthenticationSessionModel. It looks up what the browser flow should be, then starts executing the flow.
-
The flow looks at the cookie execution and sees that it is an alternative. It loads the cookie provider. It checks to see if the cookie provider requires that a user already be associated with the authentication session. Cookie provider does not require a user. If it did, the flow would abort and the user would see an error screen. Cookie provider then executes. Its purpose is to see if there is an SSO cookie set. If there is one set, it is validated and the UserSessionModel is verified and associated with the AuthenticationSessionModel. The Cookie provider returns a success() status if the SSO cookie exists and is validated. Since the cookie provider returned success and each execution at this level of the flow is ALTERNATIVE, no other execution is executed and this results in a successful login. If there is no SSO cookie, the cookie provider returns with a status of attempted(). This means there was no error condition, but no success either. The provider tried, but the request just wasn’t set up to handle this authenticator.
-
Next the flow looks at the Kerberos execution. This is also an alternative. The kerberos provider also does not require a user to be already set up and associated with the AuthenticationSessionModel so this provider is executed. Kerberos uses the SPNEGO browser protocol. This requires a series of challenge/responses between the server and client exchanging negotiation headers. The kerberos provider does not see any negotiate header, so it assumes that this is the first interaction between the server and client. It therefore creates an HTTP challenge response to the client and sets a forceChallenge() status. A forceChallenge() means that this HTTP response cannot be ignored by the flow and must be returned to the client. If instead the provider returned a challenge() status, the flow would hold the challenge response until all other alternatives are attempted. So, in this initial phase, the flow would stop and the challenge response would be sent back to the browser. If the browser then responds with a successful negotiate header, the provider associates the user with the AuthenticationSession and the flow ends because the rest of the executions on this level of the flow are all alternatives. Otherwise, again, the kerberos provider sets an attempted() status and the flow continues.
-
The next execution is a subflow called Forms. The executions for this subflow are loaded and the same processing logic occurs
-
The first execution in the Forms subflow is the UsernamePassword provider. This provider also does not require for a user to already be associated with the flow. This provider creates challenge HTTP response and sets its status to challenge(). This execution is required, so the flow honors this challenge and sends the HTTP response back to the browser. This response is a rendering of the Username/Password HTML page. The user enters in their username and password and clicks submit. This HTTP request is directed to the UsernamePassword provider. If the user entered an invalid username or password, a new challenge response is created and a status of failureChallenge() is set for this execution. A failureChallenge() means that there is a challenge, but that the flow should log this as an error in the error log. This error log can be used to lock accounts or IP Addresses that have had too many login failures. If the username and password is valid, the provider associated the UserModel with the AuthenticationSessionModel and returns a status of success()
-
The next execution is the OTP Form. This provider requires that a user has been associated with the flow. This requirement is satisfied because the UsernamePassword provider already associated the user with the flow. Since a user is required for this provider, the provider is also asked if the user is configured to use this provider. If user is not configured, and this execution is required, then the flow will then set up a required action that the user must perform after authentication is complete. For OTP, this means the OTP setup page. If the execution was optional, then this execution is skipped.
-
After the flow is complete, the authentication processor creates a UserSessionModel and associates it with the AuthenticationSessionModel. It then checks to see if the user is required to complete any required actions before logging in.
-
First, each required action’s evaluateTriggers() method is called. This allows the required action provider to figure out if there is some state that might trigger the action to be fired. For example, if your realm has a password expiration policy, it might be triggered by this method.
-
Each required action associated with the user that has its requiredActionChallenge() method called. Here the provider sets up an HTTP response which renders the page for the required action. This is done by setting a challenge status.
-
If the required action is ultimately successful, then the required action is removed from the user’s require actions list.
-
After all required actions have been resolved, the user is finally logged in.
Authenticator SPI Walk Through
In this section, we’ll take a look at the Authenticator interface. For this, we are going to implement an authenticator that requires that a user enter in the answer to a secret question like "What is your mother’s maiden name?". This example is fully implemented and contained in the examples/providers/authenticator directory of the demo distribution of Keycloak.
The classes you must implement are the org.keycloak.authentication.AuthenticatorFactory and Authenticator interfaces. The Authenticator interface defines the logic. The AuthenticatorFactory is responsible for creating instances of an Authenticator. They both extend a more generic Provider and ProviderFactory set of interfaces that other Keycloak components like User Federation do.
Packaging Classes and Deployment
You will package your classes within a single jar.
This jar must contain a file named org.keycloak.authentication.AuthenticatorFactory
and must be contained in the META-INF/services/
directory of your jar.
This file must list the fully qualified classname of each AuthenticatorFactory implementation you have in the jar.
For example:
org.keycloak.examples.authenticator.SecretQuestionAuthenticatorFactory
org.keycloak.examples.authenticator.AnotherProviderFactory
This services/ file is used by Keycloak to scan the providers it has to load into the system.
To deploy this jar, just copy it to the providers directory.
Implementing an Authenticator
When implementing the Authenticator interface, the first method that needs to be implemented is the requiresUser() method. For our example, this method must return true as we need to validate the secret question associated with the user. A provider like kerberos would return false from this method as it can resolve a user from the negotiate header. This example, however, is validating a specific credential of a specific user.
The next method to implement is the configuredFor() method. This method is responsible for determining if the user is configured for this particular authenticator. For this example, we need to check if the answer to the secret question has been set up by the user or not. In our case we are storing this information, hashed, within a UserCredentialValueModel within the UserModel (just like passwords are stored). Here’s how we do this very simple check:
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return session.users().configuredForCredentialType("secret_question", realm, user);
}
The configuredForCredentialType() call queries the user to see if it supports that credential type.
The next method to implement on the Authenticator is setRequiredActions(). If configuredFor() returns false and our example authenticator is required within the flow, this method will be called. It is responsible for registering any required actions that must be performed by the user. In our example, we need to register a required action that will force the user to set up the answer to the secret question. We will implement this required action provider later in this chapter. Here is the implementation of the setRequiredActions() method.
@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
user.addRequiredAction("SECRET_QUESTION_CONFIG");
}
Now we are getting into the meat of the Authenticator implementation. The next method to implement is authenticate(). This is the initial method the flow invokes when the execution is first visited. What we want is that if a user has answered the secret question already on their browser’s machine, then the user doesn’t have to answer the question again, making that machine "trusted". The authenticate() method isn’t responsible for processing the secret question form. Its sole purpose is to render the page or to continue the flow.
@Override
public void authenticate(AuthenticationFlowContext context) {
if (hasCookie(context)) {
context.success();
return;
}
Response challenge = loginForm(context).createForm("secret_question.ftl");
context.challenge(challenge);
}
The hasCookie() method checks to see if there is already a cookie set on the browser which indicates that the secret question has already been answered. If that returns true, we just mark this execution’s status as SUCCESS using the AuthenticationFlowContext.success() method and returning from the authentication() method.
If the hasCookie() method returns false, we must return a response that renders the secret question HTML form.
AuthenticationFlowContext has a form() method that initializes a Freemarker page builder with appropriate base information needed to build the form.
This page builder is called org.keycloak.login.LoginFormsProvider
.
the LoginFormsProvider.createForm() method loads a Freemarker template file from your login theme.
Additionally you can call the LoginFormsProvider.setAttribute() method if you want to pass additional information to the Freemarker template.
We’ll go over this later.
Calling LoginFormsProvider.createForm() returns a JAX-RS Response object. We then call AuthenticationFlowContext.challenge() passing in this response. This sets the status of the execution as CHALLENGE and if the execution is Required, this JAX-RS Response object will be sent to the browser.
So, the HTML page asking for the answer to a secret question is displayed to the user and the user enteres in the answer and clicks submit. The action URL of the HTML form will send an HTTP request to the flow. The flow will end up invoking the action() method of our Authenticator implementation.
@Override
public void action(AuthenticationFlowContext context) {
boolean validated = validateAnswer(context);
if (!validated) {
Response challenge = context.form()
.setError("badSecret")
.createForm("secret-question.ftl");
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challenge);
return;
}
setCookie(context);
context.success();
}
If the answer is not valid, we rebuild the HTML Form with an additional error message. We then call AuthenticationFlowContext.failureChallenge() passing in the reason for the value and the JAX-RS response. failureChallenge() works the same as challenge(), but it also records the failure so it can be analyzed by any attack detection service.
If validation is successful, then we set a cookie to remember that the secret question has been answered and we call AuthenticationFlowContext.success().
The last thing I want to go over is the setCookie() method. This is an example of providing configuration for the Authenticator. In this case we want the max age of the cookie to be configurable.
protected void setCookie(AuthenticationFlowContext context) {
AuthenticatorConfigModel config = context.getAuthenticatorConfig();
int maxCookieAge = 60 * 60 * 24 * 30; // 30 days
if (config != null) {
maxCookieAge = Integer.valueOf(config.getConfig().get("cookie.max.age"));
}
... set the cookie ...
}
We obtain an AuthenticatorConfigModel from the AuthenticationFlowContext.getAuthenticatorConfig() method. If configuration exists we pull the max age config out of it. We will see how we can define what should be configured when we talk about the AuthenticatorFactory implementation. The config values can be defined within the admin console if you set up config definitions in your AuthenticatorFactory implementation.
Implementing an AuthenticatorFactory
The next step in this process is to implement an AuthenticatorFactory. This factory is responsible for instantiating an Authenticator. It also provides deployment and configuration metadata about the Authenticator.
The getId() method is just the unique name of the component. The create() method is called by the runtime to allocate and process the Authenticator.
public class SecretQuestionAuthenticatorFactory implements AuthenticatorFactory, ConfigurableAuthenticatorFactory {
public static final String PROVIDER_ID = "secret-question-authenticator";
private static final SecretQuestionAuthenticator SINGLETON = new SecretQuestionAuthenticator();
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public Authenticator create(KeycloakSession session) {
return SINGLETON;
}
The next thing the factory is responsible for is specify the allowed requirement switches. While there are four different requirement types: ALTERNATIVE, REQUIRED, OPTIONAL, DISABLED, AuthenticatorFactory implementations can limit which requirement options are shown in the admin console when defining a flow. In our example, we’re going to limit our requirement options to REQUIRED and DISABLED.
private static AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
AuthenticationExecutionModel.Requirement.REQUIRED,
AuthenticationExecutionModel.Requirement.DISABLED
};
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return REQUIREMENT_CHOICES;
}
The AuthenticatorFactory.isUserSetupAllowed() is a flag that tells the flow manager whether or not Authenticator.setRequiredActions() method will be called. If an Authenticator is not configured for a user, the flow manager checks isUserSetupAllowed(). If it is false, then the flow aborts with an error. If it returns true, then the flow manager will invoke Authenticator.setRequiredActions().
@Override
public boolean isUserSetupAllowed() {
return true;
}
The next few methods define how the Authenticator can be configured. The isConfigurable() method is a flag which specifies to the admin console on whether the Authenticator can be configured within a flow. The getConfigProperties() method returns a list of ProviderConfigProperty objects. These objects define a specific configuration attribute.
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName("cookie.max.age");
property.setLabel("Cookie Max Age");
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setHelpText("Max age in seconds of the SECRET_QUESTION_COOKIE.");
configProperties.add(property);
}
Each ProviderConfigProperty defines the name of the config property. This is the key used in the config map stored in AuthenticatorConfigModel. The label defines how the config option will be displayed in the admin console. The type defines if it is a String, Boolean, or other type. The admin console will display different UI inputs depending on the type. The help text is what will be shown in the tooltip for the config attribute in the admin console. Read the javadoc of ProviderConfigProperty for more detail.
The rest of the methods are for the admin console. getHelpText() is the tooltip text that will be shown when you are picking the Authenticator you want to bind to an execution. getDisplayType() is what text that will be shown in the admin console when listing the Authenticator. getReferenceCategory() is just a category the Authenticator belongs to.
Adding Authenticator Form
Keycloak comes with a Freemarker theme and template engine. The createForm() method you called within authenticate() of your Authenticator class, builds an HTML page from a file within your login theme: secret-question.ftl. This file should be placed in the login theme with all the other .ftl files you see for login.
Let’s take a bigger look at secret-question.ftl Here’s a small code snippet:
<form id="kc-totp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="totp" class="${properties.kcLabelClass!}">${msg("loginSecretQuestion")}</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input id="totp" name="secret_answer" type="text" class="${properties.kcInputClass!}" />
</div>
</div>
Any piece of text enclosed in ${}
corresponds to an attribute or template funtion.
If you see the form’s action, you see it points to ${url.loginAction}
.
This value is automatically generated when you invoke the AuthenticationFlowContext.form() method.
You can also obtain this value by calling the AuthenticationFlowContext.getActionURL() method in Java code.
You’ll also see ${properties.someValue}
.
These correspond to properties defined in your theme.properties file of our theme.
${msg("someValue")}
corresponds to the internationalized message bundles (.properties files) included with the login theme messages/ directory.
If you’re just using english, you can just add the value of the loginSecretQuestion
value.
This should be the question you want to ask the user.
When you call AuthenticationFlowContext.form() this gives you a LoginFormsProvider instance.
If you called, LoginFormsProvider.setAttribute("foo", "bar")
, the value of "foo" would be available for reference in your form as ${foo}
.
The value of an attribute can be any Java bean as well.
Adding Authenticator to a Flow
Adding an Authenticator to a flow must be done in the admin console. If you go to the Authentication menu item and go to the Flow tab, you will be able to view the currently defined flows. You cannot modify an built in flows, so, to add the Authenticator we’ve created you have to copy an existing flow or create your own. I’m hoping the UI is intuitive enough so that you can figure out for yourself how to create a flow and add the Authenticator.
After you’ve created your flow, you have to bind it to the login action you want to bind it to. If you go to the Authentication menu and go to the Bindings tab you will see options to bind a flow to the browser, registration, or direct grant flow.
Required Action Walkthrough
In this section we will discuss how to define a required action. In the Authenticator section you may have wondered, "How will we get the user’s answer to the secret question entered into the system?". As we showed in the example, if the answer is not set up, a required action will be triggered. This section discusses how to implement the required action for the Secret Question Authenticator.
Packaging Classes and Deployment
You will package your classes within a single jar.
This jar does not have to be separate from other provider classes but it must contain a file named org.keycloak.authentication.RequiredActionFactory
and must be contained in the META-INF/services/
directory of your jar.
This file must list the fully qualified classname of each RequiredActionFactory implementation you have in the jar.
For example:
org.keycloak.examples.authenticator.SecretQuestionRequiredActionFactory
This services/ file is used by Keycloak to scan the providers it has to load into the system.
To deploy this jar, just copy it to the providers directory.
Implement the RequiredActionProvider
Required actions must first implement the RequiredActionProvider interface. The RequiredActionProvider.requiredActionChallenge() is the initial call by the flow manager into the required action. This method is responsible for rendering the HTML form that will drive the required action.
@Override
public void requiredActionChallenge(RequiredActionContext context) {
Response challenge = context.form().createForm("secret_question_config.ftl");
context.challenge(challenge);
}
You see that RequiredActionContext has similar methods to AuthenticationFlowContext. The form() method allows you to render the page from a Freemarker template. The action URL is preset by the call to this form() method. You just need to reference it within your HTML form. I’ll show you this later.
The challenge() method notifies the flow manager that a required action must be executed.
The next method is responsible for processing input from the HTML form of the required action. The action URL of the form will be routed to the RequiredActionProvider.processAction() method
@Override
public void processAction(RequiredActionContext context) {
String answer = (context.getHttpRequest().getDecodedFormParameters().getFirst("answer"));
UserCredentialValueModel model = new UserCredentialValueModel();
model.setValue(answer);
model.setType(SecretQuestionAuthenticator.CREDENTIAL_TYPE);
context.getUser().updateCredentialDirectly(model);
context.success();
}
The answer is pulled out of the form post. A UserCredentialValueModel is created and the type and value of the credential are set. Then UserModel.updateCredentialDirectly() is invoked. Finally, RequiredActionContext.success() notifies the container that the required action was successful.
Implement the RequiredActionFactory
This class is really simple. It is just responsible for creating the required actin provider instance.
public class SecretQuestionRequiredActionFactory implements RequiredActionFactory {
private static final SecretQuestionRequiredAction SINGLETON = new SecretQuestionRequiredAction();
@Override
public RequiredActionProvider create(KeycloakSession session) {
return SINGLETON;
}
@Override
public String getId() {
return SecretQuestionRequiredAction.PROVIDER_ID;
}
@Override
public String getDisplayText() {
return "Secret Question";
}
The getDisplayText() method is just for the admin console when it wants to display a friendly name for the required action.
Enable Required Action
The final thing you have to do is go into the admin console. Click on the Authentication left menu. Click on the Required Actions tab. Click on the Register button and choose your new Required Action. Your new required action should now be displayed and enabled in the required actions list.
Modifying/Extending the Registration Form
It is entirely possible for you to implement your own flow with a set of Authenticators to totally change how regisration is done in Keycloak. But what you’ll usually want to do is just add a little bit of validation to the out of the box registration page. An additional SPI was created to be able to do this. It basically allows you to add validation of form elements on the page as well as to initialize UserModel attributes and data after the user has been registered. We’ll look at both the implementation of the user profile registration processing as well as the registration Google Recaptcha plugin.
Implementation FormAction Interface
The core interface you have to implement is the FormAction interface. A FormAction is responsible for rendering and processing a portion of the page. Rendering is done in the buildPage() method, validation is done in the validate() method, post validation operations are done in success(). Let’s first take a look at buildPage() method of the Recaptcha plugin.
@Override
public void buildPage(FormContext context, LoginFormsProvider form) {
AuthenticatorConfigModel captchaConfig = context.getAuthenticatorConfig();
if (captchaConfig == null || captchaConfig.getConfig() == null
|| captchaConfig.getConfig().get(SITE_KEY) == null
|| captchaConfig.getConfig().get(SITE_SECRET) == null
) {
form.addError(new FormMessage(null, Messages.RECAPTCHA_NOT_CONFIGURED));
return;
}
String siteKey = captchaConfig.getConfig().get(SITE_KEY);
form.setAttribute("recaptchaRequired", true);
form.setAttribute("recaptchaSiteKey", siteKey);
form.addScript("https://www.google.com/recaptcha/api.js");
}
The Recaptcha buildPage() method is a callback by the form flow to help render the page. It receives a form parameter which is a LoginFormsProvider. You can add additional attributes to the form provider so that they can be displayed in the HTML page generated by the registration Freemarker template.
The code above is from the registration recaptcha plugin. Recaptcha requires some specific settings that must be obtained from configuration. FormActions are configured in the exact same was as Authenticators are. In this example, we pull the Google Recaptcha site key from configuration and add it as an attribute to the form provider. Our regstration template file can read this attribute now.
Recaptcha also has the requirement of loading a javascript script. You can do this by calling LoginFormsProvider.addScript() passing in the URL.
For user profile processing, there is no additional information that it needs to add to the form, so its buildPage() method is empty.
The next meaty part of this interface is the validate() method. This is called immediately upon receiving a form post. Let’s look at the Recaptcha’s plugin first.
@Override
public void validate(ValidationContext context) {
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
List<FormMessage> errors = new ArrayList<>();
boolean success = false;
String captcha = formData.getFirst(G_RECAPTCHA_RESPONSE);
if (!Validation.isBlank(captcha)) {
AuthenticatorConfigModel captchaConfig = context.getAuthenticatorConfig();
String secret = captchaConfig.getConfig().get(SITE_SECRET);
success = validateRecaptcha(context, success, captcha, secret);
}
if (success) {
context.success();
} else {
errors.add(new FormMessage(null, Messages.RECAPTCHA_FAILED));
formData.remove(G_RECAPTCHA_RESPONSE);
context.validationError(formData, errors);
return;
}
}
Here we obtain the form data that the Recaptcha widget adds to the form. We obtain the Recaptcha secret key from configuration. We then validate the recaptcha. If successful, ValidationContext.success() is called. If not, we invoke ValidationContext.validationError() passing in the formData (so the user doesn’t have to re-enter data), we also specify an error message we want displayed. The error message must point to a message bundle property in the internationalized message bundles. For other registration extensions validate() might be validating the format of a form element, i.e. an alternative email attribute.
Let’s also look at the user profile plugin that is used to validate email address and other user information when registering.
@Override
public void validate(ValidationContext context) {
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
List<FormMessage> errors = new ArrayList<>();
String eventError = Errors.INVALID_REGISTRATION;
if (Validation.isBlank(formData.getFirst((RegistrationPage.FIELD_FIRST_NAME)))) {
errors.add(new FormMessage(RegistrationPage.FIELD_FIRST_NAME, Messages.MISSING_FIRST_NAME));
}
if (Validation.isBlank(formData.getFirst((RegistrationPage.FIELD_LAST_NAME)))) {
errors.add(new FormMessage(RegistrationPage.FIELD_LAST_NAME, Messages.MISSING_LAST_NAME));
}
String email = formData.getFirst(Validation.FIELD_EMAIL);
if (Validation.isBlank(email)) {
errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.MISSING_EMAIL));
} else if (!Validation.isEmailValid(email)) {
formData.remove(Validation.FIELD_EMAIL);
errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.INVALID_EMAIL));
}
if (context.getSession().users().getUserByEmail(email, context.getRealm()) != null) {
formData.remove(Validation.FIELD_EMAIL);
errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.EMAIL_EXISTS));
}
if (errors.size() > 0) {
context.validationError(formData, errors);
return;
} else {
context.success();
}
}
As you can see, this validate() method of user profile processing makes sure that the email, first, and last name are filled in in the form. It also makes sure that email is in the right format. If any of these validations fail, an error message is queued up for rendering. Any fields in error are removed from the form data. Error messages are represented by the FormMessage class. The first parameter of the constructor of this class takes the HTML element id. The input in error will be highlighted when the form is re-rendered. The second parameter is a message reference id. This id must correspond to a property in one of the localized message bundle files. in the theme.
After all validations have been processed then, the form flow then invokes the FormAction.success() method. For recaptcha this is a no-op, so we won’t go over it. For user profile processing, this method fills in values in the registered user.
@Override
public void success(FormContext context) {
UserModel user = context.getUser();
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
user.setFirstName(formData.getFirst(RegistrationPage.FIELD_FIRST_NAME));
user.setLastName(formData.getFirst(RegistrationPage.FIELD_LAST_NAME));
user.setEmail(formData.getFirst(RegistrationPage.FIELD_EMAIL));
}
Pretty simple implementation. The UserModel of the newly registered user is obtained from the FormContext. The appropriate methods are called to initialize UserModel data.
Finally, you are also required to define a FormActionFactory class. This class is implemented similarly to AuthenticatorFactory, so we won’t go over it.
Packaging the Action
You will package your classes within a single jar.
This jar must contain a file named org.keycloak.authentication.FormActionFactory
and must be contained in the META-INF/services/
directory of your jar.
This file must list the fully qualified classname of each FormActionFactory implementation you have in the jar.
For example:
org.keycloak.authentication.forms.RegistrationProfile
org.keycloak.authentication.forms.RegistrationRecaptcha
This services/ file is used by Keycloak to scan the providers it has to load into the system.
To deploy this jar, just copy it to the providers directory.
Adding FormAction to the Registration Flow
Adding an FormAction to a registration page flow must be done in the admin console. If you go to the Authentication menu item and go to the Flow tab, you will be able to view the currently defined flows. You cannot modify an built in flows, so, to add the Authenticator we’ve created you have to copy an existing flow or create your own. I’m hoping the UI is intuitive enough so that you can figure out for yourself how to create a flow and add the FormAction.
Basically you’ll have to copy the registration flow. Then click Actions menu to the right of the Registration Form, and pick "Add Execution" to add a new execution. You’ll pick the FormAction from the selection list. Make sure your FormAction comes after "Registration User Creation" by using the down errors to move it if your FormAction isn’t already listed after "Registration User Creation". You want your FormAction to come after user creation because the success() method of Regsitration User Creation is responsible for creating the new UserModel.
After you’ve created your flow, you have to bind it to registration. If you go to the Authentication menu and go to the Bindings tab you will see options to bind a flow to the browser, registration, or direct grant flow.
Modifying Forgot Password/Credential Flow
Keycloak also has a specific authentication flow for forgot password, or rather credential reset initiated by a user. If you go to the admin console flows page, there is a "reset credentials" flow. By default, Keycloak asks for the email or username of the user and sends an email to them. If the user clicks on the link, then they are able to reset both their password and OTP (if an OTP has been set up). You can disable automatic OTP reset by disabling the "Reset OTP" authenticator in the flow.
You can add additional functionality to this flow as well. For example, many deployments would like for the user to answer one or more secret questions in additional to sending an email with a link. You could expand on the secret question example that comes with the distro and incorporate it into the reset credential flow.
One thing to note if you are extending the reset credentials flow. The first "authenticator" is just a page to obtain the username or email. If the username or email exists, then the AuthenticationFlowContext.getUser() will return the located user. Otherwise this will be null. This form WILL NOT re-ask the user to enter in an email or username if the previous email or username did not exist. You need to prevent attackers from being able to guess valid users. So, if AuthenticationFlowContext.getUser() returns null, you should proceed with the flow to make it look like a valid user was selected. I suggest that if you want to add secret questions to this flow, you should ask these questions after the email is sent. In other words, add your custom authenticator after the "Send Reset Email" authenticator.
Modifying First Broker Login Flow
First Broker Login flow is used during first login with some identity provider.
Term First Login
means that there is not yet existing Keycloak account linked with the particular authenticated identity provider account.
For more details about this flow see the Identity Brokering
chapter in Server Administration .
Authentication of clients
Keycloak actually supports pluggable authentication for OpenID Connect client applications.
Authentication of client (application) is used under the hood by the Keycloak adapter during sending any backchannel requests
to the Keycloak server (like the request for exchange code to access token after successful authentication or request to refresh token).
But the client authentication can be also used directly by you during Direct Access grants
(represented by OAuth2 Resource Owner Password Credentials Flow
)
or during Service account
authentication (represented by OAuth2 Client Credentials Flow
).
For more details about Keycloak adapter and OAuth2 flows see Securing Applications and Services Guide.
Default implementations
Actually Keycloak has 2 builtin implementations of client authentication:
- Traditional authentication with client_id and client_secret
-
This is default mechanism mentioned in the OpenID Connect or OAuth2 specification and Keycloak supports it since it’s early days. The public client needs to include
client_id
parameter with it’s ID in the POST request (so it’s defacto not authenticated) and the confidential client needs to includeAuthorization: Basic
header with the clientId and clientSecret used as username and password. - Authentication with signed JWT
-
This is based on the JWT Bearer Token Profiles for OAuth 2.0 specification. The client/adapter generates the JWT and signs it with his private key. The Keycloak then verifies the signed JWT with the client’s public key and authenticates client based on it.
See the demo example and especially the examples/preconfigured-demo/product-app
for the example application showing
the application using client authentication with signed JWT.
Implement your own client authenticator
For plug your own client authenticator, you need to implement few interfaces on both client (adapter) and server side.
- Client side
-
Here you need to implement
org.keycloak.adapters.authentication.ClientCredentialsProvider
and put the implementation either to:-
your WAR file into WEB-INF/classes . But in this case, the implementation can be used just for this single WAR application
-
Some JAR file, which will be added into WEB-INF/lib of your WAR
-
Some JAR file, which will be used as jboss module and configured in jboss-deployment-structure.xml of your WAR. In all cases, you also need to create the file
META-INF/services/org.keycloak.adapters.authentication.ClientCredentialsProvider
either in the WAR or in your JAR.
-
- Server side
-
Here you need to implement
org.keycloak.authentication.ClientAuthenticatorFactory
andorg.keycloak.authentication.ClientAuthenticator
. You also need to add the fileMETA-INF/services/org.keycloak.authentication.ClientAuthenticatorFactory
with the name of the implementation classes. See authenticators for more details.


Welcome to the Emlogis scheduler workflow testing tool. This application provides a REST API for a very basic functionality testings. The following paths are supported for POST/GET requests:
The request body must be a JSON object similar to the above ("result" is of course optional). The response will be JSON as above.


Web Developer, Business Analyst, Founder
Certified Full Stack Developer - The University of Toronto.
Honors Bachelor of Business Administration - Wilfrid Laurier University.
In pursuit of my HBBA, I was able to attain valuable skills such as relationship development, business development, strategic analysis and product/service management. I will forever cherish my four year journey at WLU, as it has propelled me into becoming an ambitious, courageous and calculated individual who understands the importance of creating long sustainable relationships in the pursuit of dynamic business expansion. While at U of T, I mastered the art of front-end and back-end web design, and was granted the ability to build fully functioning websites and applications for companies in any industry.
I have many years of accounting and finance work experience under my belt, allowing me to understand the importance of implementing a sustainable strategic direction to a business. During my various employment positions, I was able to identify one key metric that proved critical to healthy business growth: Strategic Cost Analysis. More importantly, the ability to procure the biggest possible margin on investments when considering overall brand development.
Many businesses pay a substantial premium to receive web/app development support. In choosing my expertise for all of your website and application needs, you will be able to effectively develop your online brand using the newest forms of software and techniques at an industry leading low price.
Let others see your company the way you see it and they will believe in it as much as you do.
Please see my Portfolio for all recent works.


TODO: Update thor to allow for git-style CLI (git bisect run)
Sets the default task when thor is executed without an explicit task to be called.
| meth<Symbol>: | name of the defaut task |
Defines the usage and the description of the next task.
usage<String> description<String>
Maps an input to a task. If you define:
map "-T" => "list"
Running:
thor -T
Will invoke the list task.
| Hash[String|Array => Symbol]: | Maps the string or the strings in the array to the given task. |
Adds an option to the set of method options. If :for is given as option, it allows you to change the options from a previous defined task.
def previous_task # magic end method_option :foo => :bar, :for => :previous_task def next_task # magic end
| name<Symbol>: | The name of the argument. |
| options<Hash>: | Described below. |
:desc - Description for the argument. :required - If the argument is required or not. :default - Default value for this argument. It cannot be required and have default values. :aliases - Aliases for this option. :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean. :banner - String to show on usage notes.
Declares the options for the next task to be declared.
| Hash[Symbol => Object]: | The hash key is the name of the option and the value |
is the type of the option. Can be :string, :array, :hash, :boolean, :numeric or :required (string). If you give a value, the type of the value is used.
Parses the task and options from the given args, instantiate the class and invoke the task. This method is used when the arguments must be parsed from an array. If you are inside Ruby and want to use a Thor class, you can simply initialize it:
script = MyScript.new(args, options, config) script.invoke(:task, first_arg, second_arg, third_arg)
The banner for this class. You can customize it if you are invoking the thor class by another ways which is not the Thor::Runner. It receives the task that is going to be invoked and a boolean which indicates if the namespace should be displayed as arguments.


|
This technical report defines version 0.2 of the RTM RDF to Topic Maps mapping vocabulary.
Please note that this document is not a formal specification from any recognized standards body, but a Technical Report published by Ontopia, a commercial company, for the convenience of all interested parties. The specification may be taken up by some standards body at some point, but no initiatives in this direction have been taken as yet.
RTM is a vocabulary that can be used to describe the mapping of an RDF vocabulary to topic maps in such a way that RDF data using that vocabulary can be converted automatically to topic maps. This allows RDF vocabularies to be used both as RDF vocabularies and as topic map vocabularies.
The vocabulary annotates RDF properties to indicate which topic map construct the property is to be mapped to. The type and scope of the resulting construct can be controlled, and for associations the role played by the subject and the object must be indicated. The RTM mapping declarations can be stored as part of the data to be mapped, or it can be stored externally.
The background and rationale for the design of this vocabulary can be found in the paper Living with topic maps and RDF.
Throughout this document the convention rtm:foo should be taken to mean the URI reference http://psi.ontopia.net/rdf2tm/#foo. Each of these URI references are published subject identifiers that together make up the RTM vocabulary.
RTM consists of the following RDF properties, as well as the individual resources defined in section 3. The subjects of statements using these properties are always RDF properties whose mapping to topic maps is to be described.
This property defines which topic map construct an RDF property is converted to during RDF-to-topic map mapping. RDF properties for which no rtm:maps-to statement is found are ignored. The possible values of this predicate are enumerated in section 3.
This property is used to override the default type otherwise assigned to topic map constructs created by the mapping.
This property is used to add a topic to the scope of topic map constructs created by the mapping.
This property is used to indicate the association role type played by the subject of an RDF statement mapped to an association.
This property is used to indicate the association role type played by the object of an RDF statement mapped to an association.
The input to the mapping consists of:
An RDF model containing the data to be mapped to a topic map, known as the input model.
An RDF model containing the mapping information, known as the mapping model. This may be the same model as the input model, but need not be.
The topic map into which the data is to be mapped. It may be empty, but need not be.
For precision, the topic map is here described as an instance of the Topic Maps Data Model defined in [TMDM], while the RDF models are described as RDF graphs, as defined by [RDF].
The mapping is done by traversing all the triples in the input model and for each triple:
Let s1 be the subject of that triple, p1 the predicate, and o1 the object.
Look for a triple in the mapping model whose subject is p1 and whose predicate is rtm:maps-to. If no such triple is found, move on to the next triple of the input model. If more than one such triple is found, select one arbitrarily and let o2 be the object of that predicate.
If s1 is a URI reference, get the topic produced by the procedure in section 3.10 for that URI reference and let it be known as t1.
If s1 is a blank node, and it has not been processed before, create a topic item to represent it and add it to the [topics] property of the topic map item. If it has been processed before, find the topic item created for it. Let this topic item be known as t1.
Now follow the instructions in the section below whose name corresponds to the value of o2. It is an error if there is no such section, or if o2 is a literal.
Create a topic name item and add it to the [topic names] property of t1. Set its [value] property to the string value of o1. It is an error if o1 is not a literal.
Finally, let the topic name item be known as c and follow the procedure in section 3.8. Implementations which support typed names must also follow the procedure in section 3.9.
Create an occurrence item and add it to the [occurrences] property of t1. If o1 is a literal, set its [value] property to the string value of o1, otherwise create a locator item and set its [notation] property to "URI" and its [reference] property to o1, and set the locator as the value of the occurrence item's [locator] property. It is an error if o1 is a blank node.
Finally, let the occurrence item be known as c and follow the procedures in section 3.8 and section 3.9.
Create an association item and add it to the [associations] property of the topic map item. Let t2 be the topic for o1; it is an error if o1 is a literal.
Look for a triple in the mapping model whose subject is p1 and whose predicate is rtm:subject-role; let rt1 be the object. It is an error if rt1 is a blank node or a literal. Create an association role item and add it to the [roles] property of the association, set its [player] property to t1, and its [type] property to the topic for rt1.
Now look for a triple in the mapping model whose subject is p1 and whose predicate is rtm:object-role; let rt2 be the object. It is an error if rt2 is a blank node or a literal. Create an association role item and add it to the [roles] property of the association, set its [player] property to t2, and its [type] property to the topic for rt2.
Finally, let the association item be known as c and follow the procedures in section 3.8 and section 3.9.
Create an association item. Get the topic for http://psi.topicmaps.org/sam/1.0/#type-instance and put it in the [type] property of the association item.
Create an association role item. Get the topic for http://psi.topicmaps.org/sam/1.0/#type and put it in its [type] property. Get the topic for o1 and put it in the [player] property. It is an error if o1 is a literal.
Create an association role item. Get the topic for http://psi.topicmaps.org/sam/1.0/#instance and put it in its [type] property, and put t1 in the [player] property.
Finally, let the association item be known as c and follow the procedure in section 3.9.
The URIs used here are the ones given in the current draft of TMDM, but they are unlikely to be the URIs in the final version. It is recommended that implementations represent RDF statements mapped to instance-of using their internal representation for the type-instance relationship. This document will be updated when TMDM is finalized.
Create a locator item and set its [notation] property to "URI" and its [reference] property to o1, then add it to the [subject identifiers] property of t1. It is an error if o1 is a literal or a blank node.
Create a locator item and set its [notation] property to "URI" and its [reference] property to o1, then set it as the value of the [subject locator] property of t1. It is an error if o1 is a literal or a blank node, or if t1 already has a different value in its [subject locator] property.
Create a locator item and set its [notation] property to "URI" and its [reference] property to o1, then add it to the [source locators] property of t1. It is an error if o1 is a literal or a blank node.
For each triple whose subject is p1 and whose predicate is rtm:in-scope, get the topic for the object of the triple and add it to the [scope] property of c. It is an error if the object is a literal.
Look for a triple whose subject is p1 and whose predicate is rtm:type. If such a triple is found, get the topic for the object of the triple and set it as the value of the [type] property of c. It is an error if the object is a literal.
If not such triple is found, get the topic for p1 and set it as the value of the [type] property of c.
Look for a topic item whose [subject identifiers] property contains a locator item whose [notation] property is "URI" and whose [reference] property contains the URI reference. If no such topic item is found, create one.
This appendix provides an RDF Schema and OWL schema for the RTM vocabulary.
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:owl="http://www.w3.org/2002/07/owl#" xmlns:rtm="http://psi.ontopia.net/rdf2tm/#"> <!-- ===== ONTOLOGY ===================================================== --> <owl:Ontology rdf:about="http://psi.ontopia.net/rdf2tm/"> <rdfs:label>RTM: RDF to topic map mapping</rdfs:label> <rdfs:comment>A vocabulary for expressing mappings from RDF vocabularies to topic map forms of the same vocabularies.</rdfs:comment> <rdfs:seeAlso rdf:resource="http://www.ontopia.net/topicmaps/materials/tmrdf.html"/> <owl:imports rdf:resource="http://www.w3.org/2000/01/rdf-schema"/> <owl:imports rdf:resource="http://www.w3.org/2002/07/owl"/> </owl:Ontology> <!-- ~~~~~ CONSTRUCT --> <owl:Class rdf:about="http://psi.ontopia.net/rdf2tm/#Construct" rdfs:label="Construct" rdfs:comment="A topic map construct to which an RDF property may be mapped."> <rdfs:isDefinedBy rdf:resource="http://psi.ontopia.net/rdf2tm/"/> <owl:oneOf rdf:parseType="Collection"> <owl:Thing rdf:about="http://psi.ontopia.net/rdf2tm/#basename"/> <owl:Thing rdf:about="http://psi.ontopia.net/rdf2tm/#occurrence"/> <owl:Thing rdf:about="http://psi.ontopia.net/rdf2tm/#association"/> <owl:Thing rdf:about="http://psi.ontopia.net/rdf2tm/#source-locator"/> <owl:Thing rdf:about="http://psi.ontopia.net/rdf2tm/#subject-identifier"/> <owl:Thing rdf:about="http://psi.ontopia.net/rdf2tm/#subject-locator"/> <owl:Thing rdf:about="http://psi.ontopia.net/rdf2tm/#instance-of"/> </owl:oneOf> <!-- disjoint with pretty much everything else --> </owl:Class> <!-- ~~~~~ MAPS-TO --> <owl:FunctionalProperty rdf:about="http://psi.ontopia.net/rdf2tm/#maps-to" rdfs:label="maps to" rdfs:comment="Specifies the topic map construct to which a particular RDF property is to be mapped."> <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Property"/> <rdfs:range rdf:resource="http://psi.ontopia.net/rdf2tm/#Construct"/> <rdfs:isDefinedBy rdf:resource="http://psi.ontopia.net/rdf2tm/"/> </owl:FunctionalProperty> <!-- ~~~~~ TYPE --> <owl:FunctionalProperty rdf:about="http://psi.ontopia.net/rdf2tm/#type" rdfs:label="of type" rdfs:comment="Specifies the type of the topic map construct created from statements using this RDF property."> <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Property"/> <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/> <rdfs:isDefinedBy rdf:resource="http://psi.ontopia.net/rdf2tm/"/> </owl:FunctionalProperty> <!-- ~~~~~ IN-SCOPE --> <rdf:Property rdf:about="http://psi.ontopia.net/rdf2tm/#in-scope" rdfs:label="in scope" rdfs:comment="Specifies a topic to be added to the scope of topic map constructs created from statements with this property."> <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Property"/> <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/> <rdfs:isDefinedBy rdf:resource="http://psi.ontopia.net/rdf2tm/"/> </rdf:Property> <!-- ~~~~~ SUBJECT-ROLE --> <owl:FunctionalProperty rdf:about="http://psi.ontopia.net/rdf2tm/#subject-role" rdfs:label="subject role" rdfs:comment="Specifies the role type played by the subject in associations created from statements with this property."> <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Property"/> <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/> <rdfs:isDefinedBy rdf:resource="http://psi.ontopia.net/rdf2tm/"/> </owl:FunctionalProperty> <!-- ~~~~~ OBJECT-ROLE --> <owl:FunctionalProperty rdf:about="http://psi.ontopia.net/rdf2tm/#object-role" rdfs:label="object role" rdfs:comment="Specifies the role type played by the object in associations created from statements with this property."> <rdfs:domain rdf:resource="http://www.w3.org/2000/01/rdf-schema#Property"/> <rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/> <rdfs:isDefinedBy rdf:resource="http://psi.ontopia.net/rdf2tm/"/> </owl:FunctionalProperty> </rdf:RDF>


| DOSE: |
5.0 ml |
oral |
GHB |
(liquid) |
My wife and I experience sores in our mouths when using GHB heavily. On the weekends when we indulge for a couple days straight we develope sores in the front of inside of our mouths where the gum tissue is the softest. My obvious concerns take in the original ingredients and what effects they could have on your insides. The reaction also intrigues me because when ingesting the mixture (with a splash of orange juice) we toss it back as quickly as possible not letting it come in contact with our lips. This leads me to conclude it is an internal reaction. I've scoured the internet for reactions to GHB and GBL taking into consideration that possibly it is a solvent burn but have found nothing. We wonder if there is a toxicity issue with something that has such powerful originating ingredients, and if improper ratios during preparation could have bad results. (I've read where people ingest the GBL straight. Isn't it a corrosive solvent?)
Experience Reports are the writings and opinions of the individual authors who submit them.
Some of the activities described are dangerous and/or illegal and none are recommended by Erowid.


New Book
×
Title
Author
Number of Pages
Status
Read
Unread
Title
Author
Page Number
Read Status
Remove book


I'm not currently set up for sales on this site. All of my available photo and fractal prints can be purchased through my Deviantart.com, or Redbubble.com pages.
Due to the closing of Pottery Rowe's Frankfort Ave. location, my pottery making days are temporarily on hold.
Like my site? Need someone who can not only build your site, but who can also do the photos for your site? Please use the contact information below for any web design inquiries.
I tend to prefer electronic communication. The best way to reach me is via email at<EMAIL_ADDRESS>You can also connect with me on various social media sites by clicking on any one of the social media buttons I've provided at the bottom of each of the pages on this site.


Class representing a Vector2
Creates a Vector2
Either a number to be used as the xy value or a Vector2
A number to be used as the y value
The Magnitude of this
The normalized Vector2 of this
The Magnitude Squared of this
A Vector2 with the value of 0, 1
A Vector2 with the xy value of -1, 0
A Vector2 with the xy value of 1, 0
A Vector2 with the value of 0, -1
Performs addition on the Vector2
Performs division on the Vector2
Evaluates if two Vector2s are equal
The Vector2 to compare with
Performs multiplication on the Vector2
Normalizes the xy value of the Vector2
Finds the Point provided by the arguments
Sets the xy value of the Vector2
A number to be used as the x value
Performs subtraction on the Vector2
Finds the sum of two Vector2s
A Vector2
Finds the quotient of two Vector2s
Finds the product of two Vector2s
Finds the difference of two Vector2s
Generated using TypeDoc
Class representing a Vector2


There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour
If you want to be a part of this great cause, you can become a member of our non profit organization.
If you want to donate some money you can , even a small donation would be a great help.
We provide educational help to poor and economic backward section click here to check.
Share it to all your friends and family members so thay can take benifit from our services.


No fee checking accounts are free bank accounts that do not impose any maintenance or activity fees Carnegie Financial Service is pround to offer free checking accounts to our valued customers. Carnegie Financial Service charges no fees on these checking accounts. They are free from the following perspectives:
What's more, We makes it easy to apply our free bank account. Just fill in the online application form send us an initial deposit of any amount over $100, and you are the proud owner of a CFS free checking account. You can send eChecks, send and receive PayPal, wire transfers, and yes, even paper checks. The best part is that CFS eChecking is totally free! It is totally free from the following perspective:
By offering these, we are probably one of the best checking account service provider in the country.


Form controls automatically receive some global styling with Bootstrap:
All textual <input>
, <textarea>
, and <select>
elements with class .form-control
have a width of 100%.
Add a wrapper element .form-group
, around each form control, to ensure proper margins:


You have unrivaled strength. You can easily lift trucks, punch with the impact of a wrecking ball.
You have gained insane speed powers and now you can easily race bullets, flash across people without them even noticing. You can even run on water! But this won't necessarily help in your maths homework.


Hugo is an awesome blog generator. However, it’s primary content mode is
Markdown, and writing in org-mode
feels much better than writing in Markdown.
org-mode
can automatically convert to Markdown, but then I have to edit the
generated Markdown file to add YAML front-matter that Hugo consumes, which is
annoying.
Enter ox-hugo
. It allows you to maintain and generate Hugo-compatible Markdown
in org-mode
, and automatically generates the necessary metadata Hugo requires.
However, like all good things in life, you have to work a little to configure it
to your liking. (What’s an extension without spending a couple hours of life
figuring out exactly how to make it conform to your workflow in the pursuit of
“optimization”). This is how I configure ox-hugo
to write all my blog posts
(including this blog post!).
Setup
Installation
Install the package ox-hugo
. If you use use-package
to manage your packages,
the following snippet should do the trick.
(use-package ox-hugo
:ensure t ;Auto-install the package from Melpa (optional)
:after ox)
I would recommend going through ox-hugo
's documentation for maximum
effectiveness.
Base Directory
In a Hugo website, you might have a directory structure that looks the following
layout. From now on, we will refer to this root directory as HUGO_ROOT_DIR
.
.
├── config.toml
├── content
│ └── post
│ ├── post1.md
│ ├── new-post.md
├── content_org
│ └── post.org
├── layouts
│ └── partials
├── public
│ ├── content
│ │ └── post
Note that I have only shown the necessary parts, your actual directory structure
will have many other files. org
doesn’t care about Hugo’s directory structure,
so I added a new directory called content-org
. This is where I store all my
org
files for the blogs. We only need one org
file, named post.org.
Global Properties
Open up $(HUGO_ROOT_DIR)/content_org/post.org
in Emacs. At the very top,
insert the following (replace <your-name=here>
with your name).
#+AUTHOR: <your-name-here>
#+HUGO_BASE_DIR: ../
This sets the author to be your name, and more importantly sets where
$(HUGO_ROOT_DIR)
is located at. In this case, the root directory is simply the
parent directory. If your root directory is elsewhere, adjust HUGO_BASE_DIR
appropriately. Setting HUGO_BASE_DIR
is necessary for ox-hugo
to set where
it will be exporting files.
Next, create a headline called Posts. Underneath, add the following properties.
* Posts
:PROPERTIES:
:EXPORT_HUGO_SECTION: post
:EXPORT_HUGO_CUSTOM_FRONT_MATTER:
:END:
EXPORT_HUGO_SECTION
sets the directory under content to generate blog posts.
Here we’ve set the export directory to be post, which corresponds to
$(HUGO_ROOT_DIR)/content/post
. Under EXPORT_HUGO_CUSTOM_FRONT_MATTER
, you
can set other YAML matter that you want to apply to all posts, such as
configuring BlackFriday options (Hugo’s markdown processor).
Posts per Subtree
We’ve now setup the basics of ox-hugo
. Next, we will look at how to write a
blog post.
Most blog posts intuitively are written as one file per post. However, ox-hugo
allows us to write all of our blog posts in one single file, and export to many
different files. Thus, all of my blog posts live in
$(HUGO_ROOT_DIR)/content_org/post.org
, and eack blog post is exported as many
different files under $(HUGO_ROOT_DIR)/content/post/
.
The motivation behind this lies with how org-mode
lists work. In a org-mode
list we can set TODO items, and manipulate the entries using org-mode
shortcuts. Then in a sense, we can think of a blog post as a TODO item, and
everything underneath the TODO item is the content of the blog post itself. We
can even mark a blog post as DONE to “release” it. Below is an example of the
collapsed view of the Posts tree. There are three blog posts, with one marked
as DONE, and the rest TODO or IN-PROGRESS. (To collapse a subtree in org-mode
,
press S-TAB
. You can then press TAB
on the POSTS headline to see
sub-headlines underneath it.)
* Posts
** TODO Go Essentials Part 1 :go:go_essentials:⤵
** IN-PROGRESS Microsoft Internship :microsoft:internship:⤵
** DONE How I started Using Emacs :emcas:org:⤵
Thus in a nutshell, we have the Posts top level headline, then TODO items as
secondary headlines. Each TODO item is a blog post, either finished or
unfinished. Any content underneath the secondary headlines is part of the actual
blog post. In the snippet above, I have collapsed the content, leaving only a ⤵
to indicate there is data underneath the headline. All your blog posts live
under the Posts subtree. So anytime you want to edit a blog post, you open
$(HUGO_ROOT_DIR)/content_org/post.org
and edit the appropriate subtree.
Blog Properties
We also want to be able to add some properties to an individual blog post. We can do so like this:
* Posts
** TODO Next Blog Post
:PROPERTIES:
:EXPORT_FILE_NAME: how-i-started-using-emacs
:END:
This sets the file name to be exported to. There are other properties you can
set, all detailed in the ox-hugo
docs. All the properties are translated to
Hugo YAML front-matter. In this fashion, ox-hugo
allows you to set file
specific YAML front-matter in an org-mode
-like way.
Tags
ox-hugo
can also generate the tags in the YAML front-matter. You can set the
tags in each blog headline such as the following. This saves you the trouble of
having to write the tabs into the PROPERTIES
themselves, since that is
slightly more troublesome. Additionally, this makes use of org-mode
tags,
allowing org-mode
to format your tags nicely.
* Posts
** TODO Next Blog Post :tag1:tag2:
These tags allow you to group related posts. Hugo can read these tags and link all blog posts with the same tags, so this is a very neat feature.
Publishing a Blog Post
To publish a blog post, set the blog post from TODO to DONE. (For those new to
org-mode
, you can do this by pressing C-c C-t
. This should bring up a side
window that allows you to set the status of the TODO item.) If you have
configured your org-mode
to output a date at which when an item was finished,
ox-hugo
will automatically set that date at the publishing date, which is much
more convenient than setting the date manually. An example is shown in the
section below.
Putting it all together
Here is an example of the Posts subtree along with two blog posts, one finished and one unfinished. The finished blog post has a CLOSED timestamp indicating the date at which it was finsihed. The unfinished blog post is considered a draft to Hugo, and so Hugo won’t compile that blog post unless Hugo is set to compile the drafts as well.
#+AUTHOR: Terrence Ho
#+HUGO_BASE_DIR: ../
* Posts
** TODO Next Blog Post
:PROPERTIES:
:EXPORT_FILE_NAME: next-blog-post-name
:END:
Introductory text... Ipsum Lorem
*** First Headline
More text... Ipsum Lorem
**** First Sub-Headline
Even more text... Ipsum Lorem
** DONE Other Finished Blog Post
CLOSED: [2019-06-17 Mon 16:53]
:PROPERTIES:
:EXPORT_FILE_NAME: other-finished-blog-post
:END:
This blog post was finished one Monday, June 17th, 2019.
Converting to Markdown
Lastly, once all this is set up, we will want to convert out posts to Markdown
files. Luckily, ox-hugo
also builds in an exporter to complement org-mode
's
exporting capabilities.
To export a subtree as a post, press C-c C-e
. This should bring up a side menu
detailing all the export methods available to you. Press H H
to export the
subtree as a file (altogether, press C-c C-e H H
). ox-hugo
determines where
to export based on the previous settings. You can also export all subtrees at
once with C-c C-e H A
. If you don’t use the option to write your blog posts as
subtrees, but rather one blog post per org
file, then use C-c C-e H h
, which
exports everything in the file to Markdown. Personally, I structure my blog
posts in the subtree style, because of how well it fits the org-mode
workflow,
but YMMV.
The exporting side view looks like the following:
Conclusion.
I hope you’ve enjoyed learning how to write blog posts with org-mode
. I’ve
honestly never enjoyed writing Markdown, and so this gives me a convenient and
fun way to write my future blog posts. Stay tuned for more similiar content!


GPIb_IX_V
Platelet membrane glycoprotein complex essential for normal platelet adhesion and clot formation at sites of vascular injury. It is composed of three polypeptides, GPIb alpha, GPIb beta, and GPIX. Glycoprotein Ib functions as a receptor for von Willebrand factor and for thrombin. Congenital deficiency of the GPIb-IX complex results in Bernard-Soulier syndrome. The platelet glycoprotein GPV associates with GPIb-IX and is also absent in Bernard-Soulier syndrome.
(mesh:D019038)
Cross-references
Synonyms
| Synonym |
Reference |
Specificity |
| Glycoprotein Ib-V-IX |
|
Exact
|
| GPIb-IX-V complex |
|
Exact
|
| Glycoprotein Ib/V/IX |
|
Exact
|
| GPIb-IX-V |
|
Exact
|
| GpIb-V-IX |
|
Exact
|
| glycoprotein Ib-V-IX complex |
|
Exact
|
Incoming Relations
Outgoing Relations
None available.


Pizza Perfect
Pizza in the Perfect Picture
Size
Crust
Sauce
Toppings
Verify Order
Contact
Size
Select your pizza size.
Personal (Ksh 599)
Medium (Ksh 999)
Large (Ksh 1299)
Extra Large (Ksh 1599)
Crust
Select your crust.
Plain
Garlic
Spicy
Special
Stuffed (+ Ksh 250)
Sauce
Select your sauce.
Marinara Sauce
White Sauce
Barbeque Sauce
No Sauce
Toppings
For the Meat Lovers!.
Ksh 100 each, first is free!
Pepperoni
Sausage
Bacon
Anchovy
Chicken
Ground Beef
For the Vegetarians.
Ksh 100 each, first is free!
Tomatoes
Onions
Olives
Green Peppers
Mushrooms
Pineapple
Spinach
Jalapeno
Feelin' cheesy?
Regular
No Cheese
Extra Cheese
Order Summary
Verify your order.

Give us your details.
So the drones don't get lost.
Thanks!
We're compiling delivery patterns and baking your pizza.


2 Kings 8:3
And it came to pass at the seven years' end, that the woman returned out of the land of the Philistines: and she went forth to cry unto the king for her house and for her land.


Zipwhip Texting
Text alerts provided by
Zipwhip
cloud texting.
Phone Number (US/Can Only)
Send Text
When done running job
When M6 tool change is hit
Send Test


Put this event on the page where you let your user login according to the code supplied to them. The `$code` is provided to the page as a parameter (for example: the user clicked on a generated link in the e-mail he received when he claimed he lost his password).
An example form could look something like this:
<form method="post" action="">
<input type="hidden" name="fields[recovery-code]" value="{$code}" />
<input type="hidden" name="fields[section]" value="users" />
<input type="submit" name="action[check-code]" value="Login" />
</form>
You can also use this feature to allow the user to reset their password. The only thing you need to do for that is supply a `password`-, an `email`- and a `confirm`-field. The e-mail address field should have the same name as the field of your users-section.
An example of this form could look something like this:
<form method="post" action="">
E-mail addres: <input type="text" name="fields[e-mail-address]" />

New password: <input type="text" name="fields[password]" />

Confirm: <input type="text" name="fields[confirm]" />

<input type="hidden" name="fields[recovery-code]" value="{$code}" />
<input type="hidden" name="fields[section]" value="users" />
<input type="submit" name="action[check-code]" value="Change password" />
</form>


Preparation time
1-2 hours
Cooking time
10 to 30 mins
Serves
Serves 6
Dietary
Homemade pastry topped with toffee, cream, banana and grated chocolate is impossible to resist in this classic American recipe.
Equipment and preparation: you will need a 20cm/8in flan tin and ceramic baking beans.
For the shortcrust pastry, put the flour into a bowl and rub in the butter with your fingertips until the mixture resembles fine breadcrumbs. Add 2-3 tablespoons of cold water gradually, mixing to form a soft dough.
Roll out the dough on a lightly floured work surface and use it to line a 20cm/8in flan tin. Cover in cling film and place in the fridge to chill for 30 minutes.
Preheat the oven to 200C/400F/Gas 6.
Line the pastry case with foil and fill with baking beans. Bake blind in the oven for about 15 minutes, then remove the beans and foil and cook for a further five minutes to dry out the base. Remove from the oven.
For the toffee filling, put the butter and sugar in a large non-stick pan. Heat gently until the butter has melted and the sugar has dissolved, then add the condensed milk. Stir continuously and evenly with a flat-ended wooden spoon for about five minutes, or until the mixture is thick and has turned a golden toffee colour – take care, as it burns easily. Turn the filling into the cooked pastry case and leave to cool and set.
For the topping, whip the double cream in a large bowl until it just holds its shape and spread evenly over the cold toffee mixture. Peel and slice the banana and dip it into a little lemon juice to prevent discolouring.
Pile the banana slices on top of the cream and sprinkle the whole pie with grated chocolate. Remove the ring of the flan and transfer to a flat plate. Serve well chilled.


Diogo Sperb Schneider Eichert, professional technologist, welcomes you to his home page.
About this web site: yes, it's just a single page that contains everything. You can take a look at the source code to learn how it's done.
Spoiler: no bulky dependencies necessary. Let's save our Ozone layer, shall we? ;)
Some video games I have published that can be played online for free:
Most of my modern games were either built with or inspired the creation of Core2D, a multiplatform 2D interaction engine.
Some of my publications:


--- layout: default title: Bootstrap Grid bodyID: gridPage ---
Basic grid layouts to get you familiar with building within the Bootstrap grid system.
There are five tiers to the Bootstrap grid system, one for each range of devices we support. Each tier starts at a minimum viewport size and automatically applies to the larger devices unless overridden.
Get three equal-width columns starting at desktops and scaling to large desktops. On mobile devices, tablets and below, the columns will automatically stack.
Get three columns starting at desktops and scaling to large desktops of various widths. Remember, grid columns should add up to twelve for a single horizontal block. More than that, and columns start stacking no matter the viewport.
Get two columns starting at desktops and scaling to large desktops.
No grid classes are necessary for full-width elements.
Per the documentation, nesting is easy—just put a row of columns within an existing column. This gives you two columns starting at desktops and scaling to large desktops, with another two (equal widths) within the larger column.
At mobile device sizes, tablets and down, these columns and their nested columns will stack.
The Bootstrap v4 grid system has five tiers of classes: xs (extra small), sm (small), md (medium), lg (large), and xl (extra large). You can use nearly any combination of these classes to create more dynamic and flexible layouts.
Each tier of classes scales up, meaning if you plan on setting the same widths for xs and sm, you only need to specify xs.


Catherine visits Pope Gregory XI in Avignon, France
Giovanni di Paolo [Public domain], via Wikimedia Commons
Stefano Maconi was one of Catherine’s later companions, not joining the group until 1376 after his mother sent him to Catherine to help diffuse a feud between him and the Tolomei and Rinaldini families, who were powerful Sienese families. He belonged to a smaller noble family in Siena. 1 He became one of her main scribes and wrote down Catherine’s theology, The Dialogue, while she dictated to him and other scribes. He was not as noble as some, and not as politically powerful, but he proved very useful in his scribal duties, he overshadowed the female scribes she initially used when he traveled with her. His friendship with Neri was deep, in modern terminology evidence shows they were “best friends”. After he joined the group, the females had to take on a new role within the group. Stefano’s dedication to Catherine continued post-mortem while he gathered the largest collection of her letters and preserved them for future generations. Many letters are addressed to him, as she sent him on missions occasionally, and Catherine liked to stay in touch with her family while they were away. He maintained political power throughout his time with Catherine as an elected official from 1373-1380. Some sources show that Stefano did not spend all of his time with Catherine, contrary to popular belief, but stayed in Siena to handle his obligation to his natal family and his political offices. This allowed him to deliver messages on behalf of Catherine in the Siena region to the government or outside affiliations. In 1381, he joined the Carthusians where he found great success, becoming prior and prior general, fulfilling Catherine’s final wish for his life.2 His life as a Carthusian kept him in a position of power until his death in 1424. Bartolomeo da Siena wrote a hagiography of him titled, De vita et moribus beati Stephani Maconi in 1626.


GOG Galaxy 2.0 is now in open beta
by Dustin BaileyGOG Galaxy 2.0 started its slow rollout earlier this year, transforming the application from a launcher for your GOG-purchased games to a way to bring all your disparate PC platforms together. While the beta has been invite-only up until this point, you can now download the new GOG Galaxy in open beta.
If you haven’t checked in with GOG Galaxy 2.0 in a while, updates have added features like global search, manual game additions, and the ability to see the online status of your friends from other platforms. The launcher still only officially supports GOG and Xbox Live, but open-source community integrations have brought support for Steam, Epic, Battle.net, Uplay, Origin, and plenty more, including emulators and individual game launchers for MMOs.
“We’re amazed and thankful for the reaction from the community to the app and taking it even further by creating integrations with 20 gaming platforms – allowing everyone to see all their games and friends in GOG Galaxy 2.0,” GOG managing director Piotr Karwowski says in a press release.
You can download the new GOG Galaxy from the official site, or check out a broad overview of its features below.
While GOG Galaxy 2.0 has been getting plenty of buzz, it’s not the only launcher in its class. Fully community-built programs like Playnite have been around for much longer and offer similar features. While the options for places to buy and play your PC games have gotten overwhelming in the past few years, the options for apps to bring those libraries together is much-appreciated.


fun getArtist(artist: String): SpotifyRestAction<Artist?>
Get Spotify catalog information for a single artist identified by their unique Spotify ID.
artist
- The com.adamratzman.spotify id or uri for the artist.
Return
Artist if valid artist id is provided, otherwise null


Much of the information on the W3C Web site is public. People use a W3C account for some interactions with the site, such as when joining a Community Group, as part of the Invited Expert process, or when completing some questionnaires.


Main
Document
Content
View
Move
Rename
Create
Delete
Block
Unblock
#create#
Repository
Rename
To save a version
Restored version
Block
Unblock
Create
Apply the changes
Undo the changes
Directory
Content
View
Move
Rename
Create directory
Delete a directory
Block
Unblock
Create
Meta-data
Document
Content
Block
Unblock
Create
Change the [group] of the directory owner
Change permissions
Set document meta parameters
Directory
Content2
Block2
Unblock2
Create2
Change the [group] of the directory owner
Change permissions
Set document meta parameters
Script
Execute
To execute locally
Rename
Block
Unblock
Create
Network
HTTP/GET
HTTP/POST
Create a symbolic link
Upload using the url
Upload
Download
Help
Help
About