blob: b4f2f9ff4ed8089638e4afad7351a8e12ef45d7c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package webaction
import (
"fmt"
"github.com/apache/openwhisk-client-go/whisk"
"github.com/apache/openwhisk-wskdeploy/utils"
"github.com/apache/openwhisk-wskdeploy/wskderrors"
"github.com/apache/openwhisk-wskdeploy/wski18n"
"github.com/apache/openwhisk-wskdeploy/wskprint"
"strings"
)
//for web action support, code from wsk cli with tiny adjustments
const (
REQUIRE_WHISK_AUTH = "require-whisk-auth"
WEB_EXPORT_ANNOT = "web-export"
RAW_HTTP_ANNOT = "raw-http"
FINAL_ANNOT = "final"
TRUE = "true"
MAX_JS_INT = 1<<53 - 1
)
var webExport map[string]string = map[string]string{
"TRUE": "true",
"FALSE": "false",
"NO": "no",
"YES": "yes",
"RAW": "raw",
}
func deleteKey(key string, keyValueArr whisk.KeyValueArr) whisk.KeyValueArr {
for i := 0; i < len(keyValueArr); i++ {
if keyValueArr[i].Key == key {
keyValueArr = append(keyValueArr[:i], keyValueArr[i+1:]...)
break
}
}
return keyValueArr
}
func addKeyValue(key string, value interface{}, keyValueArr whisk.KeyValueArr) whisk.KeyValueArr {
keyValue := whisk.KeyValue{
Key: key,
Value: value,
}
return append(keyValueArr, keyValue)
}
func SetWebActionAnnotations(filePath string, action string, webMode string, annotations whisk.KeyValueArr, fetch bool) (whisk.KeyValueArr, error) {
switch strings.ToLower(webMode) {
case webExport["TRUE"]:
fallthrough
case webExport["YES"]:
return webActionAnnotations(fetch, annotations, addWebAnnotations)
case webExport["NO"]:
fallthrough
case webExport["FALSE"]:
return webActionAnnotations(fetch, annotations, deleteWebAnnotations)
case webExport["RAW"]:
return webActionAnnotations(fetch, annotations, addWebRawAnnotations)
default:
return nil, wskderrors.NewInvalidWebExportError(filePath, action, webMode, getValidWebExports())
}
}
type WebActionAnnotationMethod func(annotations whisk.KeyValueArr) whisk.KeyValueArr
func webActionAnnotations( fetchAnnotations bool, annotations whisk.KeyValueArr,
webActionAnnotationMethod WebActionAnnotationMethod) (whisk.KeyValueArr, error) {
if annotations != nil || !fetchAnnotations {
annotations = webActionAnnotationMethod(annotations)
}
return annotations, nil
}
func addWebAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr {
annotations = deleteWebAnnotationKeys(annotations)
annotations = addKeyValue(WEB_EXPORT_ANNOT, true, annotations)
annotations = addKeyValue(RAW_HTTP_ANNOT, false, annotations)
annotations = addKeyValue(FINAL_ANNOT, true, annotations)
return annotations
}
func deleteWebAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr {
annotations = deleteWebAnnotationKeys(annotations)
annotations = addKeyValue(WEB_EXPORT_ANNOT, false, annotations)
annotations = addKeyValue(RAW_HTTP_ANNOT, false, annotations)
annotations = addKeyValue(FINAL_ANNOT, false, annotations)
return annotations
}
func addWebRawAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr {
annotations = deleteWebAnnotationKeys(annotations)
annotations = addKeyValue(WEB_EXPORT_ANNOT, true, annotations)
annotations = addKeyValue(RAW_HTTP_ANNOT, true, annotations)
annotations = addKeyValue(FINAL_ANNOT, true, annotations)
return annotations
}
func deleteWebAnnotationKeys(annotations whisk.KeyValueArr) whisk.KeyValueArr {
annotations = deleteKey(WEB_EXPORT_ANNOT, annotations)
annotations = deleteKey(RAW_HTTP_ANNOT, annotations)
annotations = deleteKey(FINAL_ANNOT, annotations)
return annotations
}
func getValidWebExports() []string {
var validWebExports []string
for _, v := range webExport {
validWebExports = append(validWebExports, v)
}
return validWebExports
}
func IsWebAction(webexport string) bool {
webexport = strings.ToLower(webexport)
if len(webexport) != 0 {
if webexport == webExport["TRUE"] || webexport == webExport["YES"] || webexport == webExport["RAW"] {
return true
}
}
return false
}
func HasAnnotation(annotations *whisk.KeyValueArr, key string) bool {
return (annotations.FindKeyValue(key) >= 0)
}
func warnWebAnnotationMissingFromActionOrSequence(apiName string, actionName string, isSequence bool){
nameKey := wski18n.KEY_ACTION
i18nWarningID := wski18n.ID_WARN_API_MISSING_WEB_ACTION_X_action_X_api_X
if isSequence {
nameKey = wski18n.KEY_SEQUENCE
i18nWarningID = wski18n.ID_WARN_API_MISSING_WEB_SEQUENCE_X_sequence_X_api_X
}
warningString := wski18n.T(i18nWarningID,
map[string]interface{}{
nameKey: actionName,
wski18n.KEY_API: apiName})
wskprint.PrintOpenWhiskWarning(warningString)
}
func TryUpdateAPIsActionToWebAction(records []utils.ActionRecord, pkgName string, apiName string, actionName string, isSequence bool) error {
// if records are nil; it may be that the Action already exists at target provider OR
// this is a unit test. If the former case, we pass through and allow provider to validate
// and return an error.
if records!=nil {
action := utils.GetActionFromActionRecords(records,pkgName,actionName)
if !HasAnnotation(&action.Annotations,WEB_EXPORT_ANNOT) {
if !utils.Flags.Strict {
warnWebAnnotationMissingFromActionOrSequence(apiName,actionName,isSequence)
action.Annotations = addWebAnnotations(action.Annotations)
wskprint.PrintOpenWhiskVerbose(utils.Flags.Verbose,
fmt.Sprintf("Web Annotations to Action; result: %v\n",action.Annotations))
} else {
return wskderrors.NewInvalidWebActionError(apiName,actionName,isSequence)
}
} else {
// verify its web-export annotation value is "true", else error
if !action.WebAction() {
return wskderrors.NewInvalidWebActionError(apiName,actionName,isSequence)
}
}
}
return nil
}
func ValidateRequireWhiskAuthAnnotationValue(actionName string, value interface{}) (string, error) {
var isValid = false
var enabled = wski18n.FEATURE_DISABLED
switch value.(type) {
case string:
secureValue := value.(string)
// assure the user-supplied token is valid (i.e., for now a non-empty string)
if len(secureValue) != 0 && secureValue!="<nil>" {
isValid = true
enabled = wski18n.FEATURE_ENABLED
}
case int:
secureValue := value.(int)
// FYI, the CLI defines MAX_JS_INT = 1<<53 - 1 (i.e., 9007199254740991)
// NOTE: For JS, the largest exact integral value is 253-1, or 9007199254740991.
// In ES6, this is defined as Number MAX_SAFE_INTEGER.
// However, in JS, the bitwise operators and shift operators operate on 32-bit ints,
// so in that case, the max safe integer is 231-1, or 2147483647
// We also disallow negative integers
// NOTE: when building for 386 archs. we need to assure comparison with MAX_JS_INT does not
// "blow up" and must allow the compiler to compare an untyped int (secureValue) to effectively
// an int64... so for the comparison we MUST force a type conversion to avoid "int" size mismatch
if int64(secureValue) < MAX_JS_INT && secureValue > 0 {
isValid = true
enabled = wski18n.FEATURE_ENABLED
}
case bool:
secureValue := value.(bool)
isValid = true
if secureValue {
enabled = wski18n.FEATURE_ENABLED
}
}
if !isValid {
errMsg := wski18n.T(wski18n.ID_ERR_WEB_ACTION_REQUIRE_AUTH_TOKEN_INVALID_X_action_X_key_X_value,
map[string]interface{}{
wski18n.KEY_ACTION: actionName,
wski18n.KEY_KEY: REQUIRE_WHISK_AUTH,
wski18n.KEY_VALUE: fmt.Sprintf("%v", value)})
return errMsg, wskderrors.NewActionSecureKeyError(errMsg)
}
// Emit an affirmation that security token will be applied to the action
msg := wski18n.T(wski18n.ID_VERBOSE_ACTION_AUTH_X_action_X_value_X,
map[string]interface{}{
wski18n.KEY_ACTION: actionName,
wski18n.KEY_VALUE: enabled})
wskprint.PrintlnOpenWhiskVerbose(utils.Flags.Verbose, msg)
return msg, nil
}