authui-container/common/index.ts (52 lines of code) (raw):
/*!
* Copyright 2020 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {isNonNullObject} from './validator';
// REGEX pattern for safe URL.
const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;
/**
* The innocuous string returned when an unsafe URL is to be sanitized.
* about:invalid is registered in
* http://www.w3.org/TR/css3-values/#about-invalid.
*/
const INNOCUOUS_STRING = 'about:invalid';
/**
* Defines a new read-only property directly on an object and returns the object.
*
* @param obj The object on which to define the property.
* @param prop The name of the property to be defined or modified.
* @param value The value associated with the property.
*/
export function addReadonlyGetter(obj: object, prop: string, value: any): void {
Object.defineProperty(obj, prop, {
value,
// Make this property read-only.
writable: false,
// Include this property during enumeration of obj's properties.
enumerable: true,
});
}
/**
* Removes entries in an object whose values are undefined and returns the same
* object. This only removes the top-level undefined fields.
*
* @param obj The object whose undefined fields are to be removed.
* @return The same object with undefined fields removed.
*/
export function removeUndefinedFields<T>(obj: T): T {
// If obj is not a non-null object, return it back.
if (!isNonNullObject(obj)) {
return obj;
}
for (const key in obj) {
if (typeof obj[key] === 'undefined') {
delete obj[key];
}
}
return obj;
}
/**
* Formats a string of form 'project/{projectId}/{api}' and replaces
* with corresponding arguments {projectId: '1234', api: 'resource'}
* and returns output: 'project/1234/resource'.
*
* @param str The original string where the param need to be
* replaced.
* @param params The optional parameters to replace in the
* string.
* @return The resulting formatted string.
*/
export function formatString(str: string, params?: {[key: string]: string}): string {
let formatted = str;
Object.keys(params || {}).forEach((key) => {
formatted = formatted.replace(
new RegExp('{' + key + '}', 'g'),
(params as {[key: string]: string})[key]);
});
return formatted;
}
/**
* Maps an object's value based on the provided callback function.
*
* @param obj The object to map.
* @param cb The callback function used to compute the new mapped value.
* @return The mapped new object.
*/
export function mapObject<T, V>(
obj: {[key: string]: T},
cb: (key: string, value: T) => V): {[key: string]: V} {
const mappedObject: {[key: string]: V} = {};
Object.keys(obj).forEach((key: string) => {
mappedObject[key] = cb(key, obj[key]);
});
return mappedObject;
}
/**
* Sanitizes the URL provided.
*
* @param url The unsanitized URL.
* @return The sanitized URL.
*/
export function sanitizeUrl(url: string): string {
if (!isSafeUrl(url)) {
return INNOCUOUS_STRING;
}
return url;
}
/**
* @param url The URL to validate for safety.
* @return Whether the URL is safe to use.
*/
export function isSafeUrl(url: string): boolean {
return SAFE_URL_PATTERN.test(url);
}
/**
* @param str The string to check.
* @return Whether the string ends with a letter or number.
*/
export function isLastCharLetterOrNumber(str: string): boolean {
const lastChar = str.charAt(str.length - 1);
return !!lastChar.match(/[a-z0-9]/i);
}