CRM STNI olaTxnTestRuleHits
Views GeoJSON
Table of contents
- Summary
- Requirements
- Installation
- Usage
- Bounding Box Support
- To Do
- Credits
- Current Maintainers
Summary
Views GeoJSON is a style plugin for Views to deliver location-specific data in GeoJSON format, see RFC 7946: The GeoJSON Format.
Each row is output as a GeoJSON "Features" including geospatial data and optional metadata. All features are wrapped in a "Feature Collection" object.
Requirements
Drupal core modules
- Views
- RESTful Web Services
- Serialization
External projects
Optional Drupal modules
Installation
Install as you would normally install a contributed Drupal module.
Visit https://www.drupal.org/node/1897420 for further information.
Usage
- Create a View showing an entity (likely "Content") that includes geospatial data in its fields
- Click "Add" on the Views configuration screen and choose "GeoJSON export" from the Display type choices
- Add fields to the Display that include either: longitude and latitude coordinates, a Geofield or Geolocation field, or WKT data
- The Format for the Display should show "GeoJSON". Choose that format if not already set
- In the Format area click "Settings" (next to "GeoJSON"), and:
- Choose "Map Data Sources" specifying the type of data in your field(s) (Geofield, lat/lon, etc.)
- Assign the particular field(s) that include that data
- Optionally add fields for title and description of each point/feature, and add these to the Format settings
- Optionally set a JSONP prefix in Format settings, (see https://en.wikipedia.org/wiki/JSONP)
- Set the Path, perhaps using your site's API endpoint URL structure or ending with ".json" or ".geojson"
Bounding Box Filtering
GeoJSON views can accept a bounding box as an argument to return only the points
within that box.
It has been tested with OpenLayers' Bounding Box Strategy but should work with
any mapping tool that requests bounding box coordinates as
"?bbox=left,bottom,right,top" in the query string. Argument ID "bbox" is
default for OpenLayers but can be changed.
- Create a GeoJSON view as above in USAGE
- Add a layer to OpenLayers of type GeoJSON, at
/admin/structure/openlayers/layers/add/openlayers_layer_type_geojson,
specifying the URL to your GeoJSON feed and checking the box for "Use Bounding
Box Strategy" - In your GeoJSON View configuration, add a Contextual Filter of type:
"Custom: Bounding box" - In the Contextual Filter settings, under "When the filter value is NOT in
the URL as a normal Drupal argument", choose: "Provide default value" - In the "Type" dropdown, choose: "Bounding box from query string"
- For OpenLayers, leave "Query argument ID" as "bbox" and click Apply
To Do
- Support addditional GeoJSON feature types like LineString
- Support an optional altitude coordinate for Point positions
- Support additional coordinate systems
Credits
This module was originally born from a patch by tmcw to the
OpenLayers module and adapted
to model the
Views Datasource module.
Much of the code is drawn directly from these sources.
Current Maintainers
- Jeff Schuler (jeffschuler) - https://www.drupal.org/u/jeffschuler
Card Order
CardOrder API contains the following operations:
- Card Order (POST)
- Card Order Status (GET)
It follows the Asynchronous request-reply API pattern. The client can request the Card Order. This request will be asynchronously processed by the SAP. In the meantime the client may check the status of the Card Order request processing. When the card is ready, the corresponding status will be returned by the Card Order Status API operation.
All details about the request and response structure can be found at the API-portal.
WunschPIN capability
Among other ways of how the PIN may be set for the card ordered there is an option to pass the custom PIN code from the client.
It is unsafe to pass the PIN over the network in a plain text format. Therefore the PDI API provides a secure way to pass the PIN in encrypted format.
PIN travel throught the call stack

Pseudo Code proposal
Four major parts have to be done on the Client-Side who want to send an encrypted PIN:
The script must have these parts:
- PIN Block Generation = ConstructPINBlock(PIN, Label)
- PublicKey = ObtainPublicKey()
- Encrypt Data = EncryptData(PINBlock, PublicKey)
- Transmission Data = PrepareForTransmission(EncryptedData)
PIN Block
// Preparation : We define the PIN and Label as variables
// Label can be for example License Plate / Bestellkennung
PIN = "1234"
Label = "Transaction Label"
Now we build the actual encoded PIN Block
// We construct the PIN Block with Correct Format
Function ConstructPINBlock(PIN, Label)
// Encode the Label (BestellKennung) (e.g., ISO-8859-1)
EncodedLabel = EncodeToISO88591(Label)
// Construct the PIN block as per the detailed format
// Control field and PIN Length
// ("24" indicates a control field value of 2 and a PIN length of 4)
// PIN itself ("1234")
// Filler bytes to pad the remainder of the PIN block ("ffffffffff")
// Length of the encoded label, formatted as a 2-digit hexadecimal value
// Encoded label (BestellKennung)
PinBlockHex = "24" + PIN + "ffffffffff" + FormatAsHex(EncodedLabel.Length, 2) + HexEncode(EncodedLabel)
Return PinBlockHex
Function EncodeToISO88591(Text)
// Simulated function to encode text to ISO-8859-1 encoding
// Actual implementation will depend on the programming language and environment
Return ISO88591EncodedText
Function FormatAsHex(Number, Digits)
// Simulated function to format a number as a hexadecimal string with a specified number of digits
// Actual implementation will depend on the programming language and environment
Return HexFormattedNumber
Function HexEncode(Data)
// Simulated function to encode data as a hexadecimal string
// Actual implementation will depend on the programming language and environment
Return HexEncodedData
Public key
The Public key will be shared with the Client who wants to send an encrypted PIN.
It can be done for instance by the email.
It is safe to share Public Keys and pass it over the network.
Public Key is used to encrypt the PIN.
The Public Key must be available in the script.
This can be done in several ways. Like
- Embedding Public Key in the script (not recommended)
- Storing and access Public Key in a secure secrets storage like Hashicorp Vault, Amazon KeyVault etc. or in other safe place in the Infrastructure of the Client.
Encrypt data
Based on the prepared PIN Block and the Public Key, the Client can now encrypt the Data before sending it via API.
// Encrypt the Data
Function EncryptData(Data, PublicKey)
// Use RSA encryption with OAEP padding and SHA-256 hashing
EncryptedData = RSAEncrypt(Data, PublicKey, "OAEPWithSHA256AndMGF1Padding")
Return EncryptedData
Transmission of the Encrypted Data
It is necessary to convert the encrypted data into a format that can be easily transported via REST APIs.
// Prepare for Transmission
Function PrepareForTransmission(EncryptedData)
// Convert encrypted data to a hexadecimal string or base64
FormattedData = ConvertToHex(EncryptedData)
Return FormattedData
Examples
Java code
package com.src.dkvws;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class C_DkvCalcPinBlockManuell {
public static void main( String[] args ) throws Exception {
// Public RSA-Key
BigInteger modulus = new BigInteger( ""
+ "9A534E385738A0B7F063A2AF685EBF8D2C938A56B0CF0812BCC355676C8366A6"
+ "4EE211DC8D02810D39B9E00DBF0A384BE0DC33315FC936DC26A1784E9773BADE"
+ "C710A5FAED6023953E5A18CDC73B54146E0E738CB8413693B07F2195C164912B"
+ "BF397B3DAFEBF1D693C3917CF2F6DD38D81529D5E08B690E3267F71B51ACE189"
+ "E62B696859A8A8C0124738E306772522B914B38D77B6BF85D92A569A10D15B10"
+ "2C8213688C07F735AF57D1D5FF3822781E1AA08289EDAA874FB543498721C3C0"
+ "F678CB58BA974A04352A3AA84656CA8F76333C6D9E4FBF8F5A66D1ABC64F95C5"
+ "01BFED2C7BF2624C3DA29A51089047E900D931DF7BEF22159AA0043991C575EC"
+ "3E40D2BEF395D052AD6CCE3E7B1D26BED81CCE70EFFC0DE13B364A18C316B17D"
+ "81D823E32F744AB525EDC51ADB2B0C8AAFA73A5BCC10FA03233C672743681A6D"
+ "58FE15DE11108D910C84B081AA69AE1C821D86F0C50387E54AEC04E5E933AFA9"
+ "1FD93FB0E237647C6CB9C45158689CF23004D2022FE2C8AB352858A613A67655"
+ "C5AC206956239055C658FF287DD5C5436D90F7C929A904FE3CE59607C2F2969F"
+ "C49F926EDCC16B7C9CA45C383D0F4C16FABC4DAB82D567BEFFA1D1B23E8EDF69"
+ "D2A6B9A9CDA0678041413210E4BA62C30E79570F875E27CD2DD1C626D3009840"
+ "7EBD4232CDEA3A857C743D346EF6E440425E7E5D0B5B6796B9C44B6452211105",
16
);
BigInteger publicExponent = new BigInteger( "010001", 16 );
PublicKey publicKey = KeyFactory.getInstance( "RSA" ).generatePublic( new RSAPublicKeySpec( modulus, publicExponent ) );
// PIN-Block
String bestellKennung = "B-AA9876";
byte[] bestellKennungEncoded = bestellKennung.getBytes( "ISO-8859-1" );
String pinBlockHex =
"24" // Control field and PIN length
+ "1234" // PIN
+ "ffffffffff" // Filler
+ String.format( "%02x", bestellKennungEncoded.length ) // Length of Label
+ Hex.encodeHexString( bestellKennungEncoded ); // Label
byte[] pinBlock = Hex.decodeHex( pinBlockHex );
// Encryption-Algorithm
Security.addProvider( new BouncyCastleProvider() );
Cipher cipher = Cipher.getInstance( "RSA/NONE/OAEPWithSHA256AndMGF1Padding", "BC" );
cipher.init( Cipher.ENCRYPT_MODE, publicKey );
// Encryption
byte[] pinBlockEncrypted = cipher.doFinal( pinBlock );
// Return - as example
System.out.println( Hex.encodeHexString( pinBlockEncrypted ) );
}
}
JavaScript (TypeScript) Code (Node / Angular)
import { Inject, Injectable } from '@angular/core';
import { TextEncoder } from 'text-encoding';
import { ConfigurationFacade } from "@dkv/cockpit-framework-adapter";
export interface PinEncryptionResponse {
encryptedPin: string;
keyId: string;
}
export interface PublicKey {
key: CryptoKey;
keyId: string;
}
@Injectable({
providedIn: 'root',
})
export class PinEncryptionService {
static PIN_BLOCK_LENGTH = 8;
constructor( @Inject('SubtleCrypto') readonly crypto ) {}
/*
The alphanumeric characters of the order identifier (label) are coded using ISO-Latin-1 (ISO-8859-1) (each character is coded as one byte).
(each character is coded as one byte). The length in bytes is placed in front of the coded characters as a byte.
This results in a block of variable length (maximum 33 bytes):
<number of following bytes> <order identifier as ISO-8859-1 bytes>
*/
createBestellKennungBlock(label: string): Uint8Array {
if (label.length > 32) {
throw new Error('Label is to long max 32 chars allowed');
}
// Create array for label block
const labelBlock = new Uint8Array(label.length + 1);
let index = 0;
// Encode lable with iso-8859-1 (one byte for each char)
const encoder = new TextEncoder('iso-8859-10', {NONSTANDARD_allowLegacyEncoding: true});
const isoArray = encoder.encode(label);
// Add length to the beginning of the block
labelBlock[index] = isoArray.length;
index++;
// Add iso-8859-1 values to the block
for (const item of isoArray) {
labelBlock[index] = item;
index++;
}
return labelBlock;
}
/*
The PIN block and the order identifier (label) are concatenated to produce a byte sequence with a maximum length of 41 bytes.
length of 41 bytes. This byte sequence is encrypted with the public RSA key. For this purpose, the
encryption method "PKCS#1 V2.1 EME-OAEP "1 with SHA-256 is used.
*/
async encrypt(pin: string, label: string): Promise<PinEncryptionResponse> {
const key = await this.initKey(
ConfigurationFacade.getConfiguration().pinEncryption['publicKey']
);
const publicKey = {
key,
keyId: ConfigurationFacade.getConfiguration().pinEncryption['keyId'],
};
if (!publicKey) {
throw new Error("Couldn't get public key");
}
// Create pinBlock and labelBlock
const pinBlock = this.createPinBlock(pin);
const labelBlock = this.createBestellKennungBlock(label);
// Create array to hold the clear text
const clearTextLength = pinBlock.length + labelBlock.length;
const clearText = new Uint8Array(clearTextLength);
// Index for tracking current position in the clear text array
let index = 0;
// Add pinBlock to clear text array
for (const byte of pinBlock) {
clearText[index] = byte;
index++;
}
// Add labelBlock to clear text array
for (const byte of labelBlock) {
clearText[index] = byte;
index++;
}
// Retrieve key
const encrypted = await this.crypto.encrypt(
{name: 'RSA-OAEP', hash: {name: 'SHA-256'}},
publicKey.key,
clearText
);
return {
encryptedPin: this.asHexString(new Uint8Array(encrypted)),
keyId: publicKey.keyId,
};
}
// Converts a binary string in an ArrayBuffer
private str2ab(str): ArrayBuffer {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
private asHexString(binary: Uint8Array) {
let output = '';
for (const n of binary) {
// Convert to hex and (left-)pad with 0
output += ('0' + n.toString(16)).slice(-2);
}
return output;
}
// Ads a nibble (half-byte) at the specified index (index of the nibble that is)
private addNibble(buffer: Uint8Array, index: number, data: number) {
if (data >= 0 && data < 16) {
// First nibble of the byte gets shifted by 4 bits
const rawData = index % 2 === 0 ? data * 16 : data;
// Every two successive indexes belong to the same byte and therefore the same index
const bufferIndex = index % 2 === 0 ? index / 2 : index / 2 - 0.5;
buffer[bufferIndex] += rawData;
} else {
throw new Error('Nibble not in allowed range. A nibble (half-byte) can only have values from 0 to 15');
}
}
/*
The PIN block, consisting of 8 bytes (16 half bytes), is structured as follows:
+-+-+-+-+-+-+---+---+---+---+---+---+---+---+-+-+
|C|L|P|P|P|P|P/F|P/F|P/F|P/F|P/F|P/F|P/F|P/F|F|F|
+-+-+-+-+-+-+---+---+---+---+---+---+---+---+-+-+
Each field corresponds to a half-byte (4 bits).
C: Control field Fixed value '2' (0010)
L: PIN length Values from '4' to 'C' (here: 0100)
P: PIN number BCD coded (0000 to 1001)
F: Filler fixed value 'F' (1111)
P/F: PIN number/filler Depending on the PIN length
*/
private createPinBlock(pin: string): Uint8Array {
const pinLength = pin.length;
if (pinLength < 4 || pinLength > 12) {
throw new Error('Pin has an invalid length. Must be between 4 and 12 digits.');
}
let index = 0;
const pinBlock = new Uint8Array(8);
this.addNibble(pinBlock, index, 2);
index++;
this.addNibble(pinBlock, index, pinLength);
index++;
for (const char of pin) {
this.addNibble(pinBlock, index, +char);
index++;
}
while (index < PinEncryptionService.PIN_BLOCK_LENGTH * 2) {
this.addNibble(pinBlock, index, 15);
index++;
}
return pinBlock;
}
// TODO: Implement fetching of key from secure storage / vault
private async initKey(encodedKey: string): Promise<CryptoKey> {
// base64 decode the string to get the binary data
const binaryDerString = window.atob(encodedKey);
// convert from a binary string to an ArrayBuffer
const binaryDer = this.str2ab(binaryDerString);
return this.crypto.importKey(
'spki',
binaryDer,
{
name: 'RSA-OAEP',
hash: 'SHA-256',
},
false,
['encrypt']
);
}
}
C# code
public class PinValueCalculator{
public static string Calculate(string publicKey, string pin, string offset){
try{
// PIN block
byte[] offsetEncoded = Encoding.GetEncoding("ISO-8859-1").GetBytes(offset);
string pinBlockHex = "24" // control (fix)
+ pin // pin value
+ "ffffffffff" // filler (fix)
+ offsetEncoded.Length.ToString("X2") // length ("X2" or "%02x")
+ BitConverter.ToString(offsetEncoded).Replace("-", ""); // offset / LPN
pinBlockHex = pinBlockHex.ToLower();
byte[] pinBlock = HexStringToByteArray(pinBlockHex);
// Bouncy version
// Public RSA key
var modulus = new Org.BouncyCastle.Math.BigInteger(publicKey, 16);
var publicExponent = new Org.BouncyCastle.Math.BigInteger("010001", 16);
// Create the RSA parameters
var rsaParameters = new RsaKeyParameters(false, modulus, publicExponent);
// Bouncy version
// Verschlüsselungs-Algorithmus
IBufferedCipher cipher = CipherUtilities.GetCipher("RSA/NONE/OAEPWithSHA256AndMGF1Padding");
cipher.Init(true, rsaParameters);
// Verschlüsselung
byte[] pinBlockEncrypted = cipher.DoFinal(pinBlock);
// Output
return Hex.ToHexString(pinBlockEncrypted);
} catch (Exception e){
return e.Message + System.Environment.NewLine + e.StackTrace;
}
}
public static byte[] HexStringToByteArray(string hex)
{
byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < hex.Length; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
}