Use swift and macOS to set file attributes - macos

macOS, swift3
Apple has an API Foundation > FileManager > setAttributes(_:ofItemAtPath:)
The declaration is
func setAttributes(_ attributes: [FileAttributeKey : Any], ofItemAtPath path: String) throws
It is for setting the creation date etc for a file.
I can handle the >ofItem path:String) throws< but the first part has me stumped.
The API says it can return 'true' but swift returns void. There is an attribute named 'creationDate'. What is the significance of the underscore '_'.
I think 'attributes' is a mutable dictionary
var myAttributesDictionary = [FileAttributeKey : Date]()
myAttributesDictionary[FileAttributeKey.creationDate] = myDateObject
let fm = FileManger()
let xxx = fm.setAttributes(myAttributesDictionary:[FileAttributeKey : creationDate], ofItemAtPath myPath)
I have tried many variations and now I am stumped and I do not know what is required. I cannot get the setAttributes line to compile

I spent some time looking for an answer before posting the question.
When I went looking for my questionI found an answer.
let mypath = "/path/to/file"
let myDateObject = NSDate() // NSDate() is todays date
let attributes = [FileAttributeKey.creationDate: myDateObject]
do {
try FileManager.default.setAttributes(attributes, ofItemAtPath: myPath)
}
catch
{
print(error)
}

Related

reading data from multiple tabs in excel

I have some data in multiple tabs in excel and those need to be read using cypress. Is there a way to do it? The only suggested way to read data from excel is to convert to JSON file. So, I referred and wrote this in ./cypress/support/command.js file with an intention that each tab will be stored as JSON file.
Cypress.Commands.add('dataFile', (excelFile) => {
let XLSX = require('xlsx');
let fs = require('fs');
let XLBook = XLSX.readFile('cypress/fixtures/' + excelFile);
let XLTabs = XLBook.SheetName;
for(let i = 0; i < XLTabs.length; i++)
{
let XLTabName = XLTabs[i].toString();
let XLData = XLSX.utils.sheet_to_json(XLBook.Sheets[XLTabs[i]);
fs.writeFile('./cypress/fixtures/jSONData.json', JSON.stringify(XLData , null, 2), (e: string) => {
if(e) {
console.log(e);
}
});
}
});
I used the below command in my test to use it.
cy.dataFile('File1.xlsx');
But, the test was highlighting this error:
Property 'dataFile' does not exist on type 'cy & EventEmitter'.ts(2339)
Please read Types for custom commands to resolve the error
Property 'dataFile' does not exist on type 'cy & EventEmitter'.ts(2339)
But you cannot use a custom command, because both let XLSX = require('xlsx') and let fs = require('fs') must be used in a task.
Please see this answer Read excel files in Cypress for the broad strokes.

Nim Slack bot Signature Verification Issues

I'm fairly new to Nim, and I suspect I'm just doing something wrong here. I'm using Jester (for routing, etc) and Nimcrytpo (for hmac) but something isn't adding up. Here's how I'm attempting to verify a signature:
import jester
import dotenv
import os, strutils, times
import nimcrypto
const timestampHeader = "X-Slack-Request-Timestamp"
const slackSignatureHeader = "X-Slack-Signature"
const signatureVersion = "v0"
const signingSecret = os.getEnv("SLACK_SIGNING_SECRET")
proc isTimestampRecent(timestamp: int): bool =
abs(getTime().toUnix - timestamp) <= (60 * 5)
proc verifySignature*(request: Request): bool =
if (not request.headers.hasKey timestampHeader) or
(not request.headers.hasKey slackSignatureHeader):
return false
let timestamp = request.headers[timestampHeader].parseInt
if not timestamp.isTimestampRecent():
return false
let baseString = signatureVersion & ':' & $timestamp & ':' & $request.body
let mySignature = sha256.hmac(signingSecret, baseString)
let slackSignature = MDigest[256].fromHex(request.headers[slackSignatureHeader])
mySignature == slackSignature
A few things I'm running into:
The signature doesn't match, and I'm not really sure how to debug that. I'm definitely getting a valid request from Slack and following the instructions for verification here: https://api.slack.com/authentication/verifying-requests-from-slack#about, but it's incorrect.
I know I'm missing the v0= in the comparison, but I'm not quite sure how to do that with the time independent comparison (whether I should be skipping that part or not in the comparison, etc)
My best guess at this point is that somehow the Jester/Httpbeast request body isn't "raw" enough (though it's just plain json...?) or is somehow processed.
Any help or suggestions on how to debug would be greatly appreciated. Thank you in advance!
After fussing with this for a while, I found I was doing a number of things incorrectly! Hopefully this helps others:
The signingSecret is pulled from the env, so it shouldn't be a constant--I moved that into the proc itself defined with let instead.
The slack signature in the headers is prefixed with v0=, which makes it the wrong length for MDigest[256].fromHex(), so that was ending up as the null value (0000...) instead of what should've been.
Here's a working version, now in case anyone else should need one. Please let me know if you see anything that could be improved as well.
import jester
import dotenv
import os, strutils, times
import nimcrypto
const timestampHeader = "X-Slack-Request-Timestamp"
const slackSignatureHeader = "X-Slack-Signature"
const signatureVersion = "v0"
proc isTimestampRecent(timestamp: int): bool =
abs(getTime().toUnix - timestamp) <= (60 * 5)
proc verifySignature*(request: Request): bool =
let signingSecret = os.getEnv("SLACK_SIGNING_SECRET")
if (not request.headers.hasKey timestampHeader) or
(not request.headers.hasKey slackSignatureHeader):
return false
let timestamp = request.headers[timestampHeader].parseInt
if not timestamp.isTimestampRecent():
return false
let baseString = signatureVersion & ':' & $timestamp & ':' & $request.body
let mySignature = sha256.hmac(signingSecret, baseString)
var rawSlackSignature: string = $request.headers[slackSignatureHeader]
rawSlackSignature.removePrefix(signatureVersion & '=')
let slackSignature = MDigest[256].fromHex(rawSlackSignature)
mySignature == slackSignature

Get system installation date programmatically

I am trying to get the system installation date by running a console app.
The only way I know how to do this is by parsing the /var/log/install.log file for the latest string containing OSInstaller & Install Complete items.
Is there a handy system API I am missing?
As a suggestion for exploration only:
You could try looking at the files /System/Library/Receipts.
You will probably see a com.apple.pkg.BaseSystemResources.plist file, its modification date may tell you when the OS was installed.
There are also com.apple.pkg.update.os.*.plist files for updates, again look at the modification dates and maybe parse the wildcard (*) bit if you can determined a naming convention.
HTH, Happy Hunting!
So in case, someone finds this useful.
Swift 3.0
SOLUTION 1
Initially I parsed /var/log/install.log file to get date string:
// To get OS installation date we'll need to check system log file and find entry which contains installation date
var systemDates : String? = nil
do {
let fullSystemLog = try NSString(contentsOf: URL(fileURLWithPath: "/var/log/install.log"), encoding: String.Encoding.utf8.rawValue)
let entries = fullSystemLog.components(separatedBy: "\n")
//Filter to get only entries about OS installation
let filtered = entries.filter{ entry in
return entry.contains("OSInstaller") && entry.contains("Install Complete") //Markers telling that OS was installed
}
var latestMention = ""
if filtered.count > 0 {
//If 1 or more entries found we'll pick last one
latestMention = filtered.last!
}
else if entries.count > 0 {
//If there are 0 mentions of OS installation - we'll use first entry in logs
latestMention = entries.first!
}
//parse picked entry for date
do {
let regex = try NSRegularExpression(pattern: ".+:[0-9]{2}", options: [])
let nsString = latestMention as NSString
let results = regex.matches(in: latestMention,
options: [], range: NSMakeRange(0, nsString.length))
let actualDateSubstrings = results.map { nsString.substring(with: $0.range)}
if let dateStringFromMention = actualDateSubstrings.first {
systemDates = dateStringFromMention
}
else {
systemDates = "<Error: no date results>"
}
} catch let error as NSError {
systemDates = "<Error: invalid regex: \(error.localizedDescription)>"
}
}
catch {
systemDates = "<Error: system log file not found>"
}
print("\tSYSTEM INSTALLED: \(systemDates)")
SOLUTION 2
The second solution appears much simpler. Look into the InstallDate field of /System/Library/Receipts/com.apple.pkg.BaseSystemResources.plist:
let systemDates = NSDictionary(contentsOfFile: "/System/Library/Receipts/com.apple.pkg.BaseSystemResources.plist")
print("\tSYSTEM INSTALLED: \(systemDates?["InstallDate"])")

SecSignVerifyTransform crashing in Swift with CSSM error Code=-2147415790

I'm trying to obtain a digital signature for a XML string using a RSA private key using Swift as command-line script (to be called from FileMaker later).
The compiler kept crashing with "segmentation fault 11" and then "Illegal Instruction: 4" and I kept drilling down until I (think) I found the problem, but it's completely beyond me, so please, please help!! ;) :)
As the title says, when I invoke SecTransformExecute on my SecSignTransform, with a binary version of my String as input attribute, I get the following error message:
Error Domain=Internal CSSM error Code=-2147415790 "The operation
couldn’t be completed. (Internal CSSM error error -2147415790 -
Internal error #80010912 at __SignTransform_block_invoke_2
/SourceCache/Security/Security-57031.1.35/Security/libsecurity_transform/lib/SecSignVerifyTransform.c:279)" UserInfo=0x7fc620e23aa0 {NSDescription=Internal error #80010912 at
__SignTransform_block_invoke_2 /SourceCache/Security/Security-57031.1.35/Security/libsecurity_transform/lib/SecSignVerifyTransform.c:279,
Originating Transform=CoreFoundationObject}
Here is the relevant part of my code:
import Foundation
import CoreFoundation
import Security
var signer: SecTransformRef
var signedData, digestData: NSData
var error: Unmanaged<CFErrorRef>?
var status: OSStatus
var key: SecKey
var anyItem: Unmanaged<AnyObject>?
var keySearchDict: [String : AnyObject]
let keyMatch = "[*place search tag here*]" as String
// turns a string into a binary to sign
let str = "Hello World"
let uintData = [UInt8](str.utf8)
let sourceData = CFDataCreate(kCFAllocatorDefault, uintData, countElements(uintData))
// sets up keySearchDict to query Keychain
keySearchDict = [(kSecClass as String): (kSecClassKey as String), (kSecMatchSubjectContains as String): keyMatch, (kSecReturnRef as String): kCFBooleanTrue]
// gets private key using keySearchDict
status = SecItemCopyMatching(keySearchDict, &anyItem)
key = (anyItem!.takeRetainedValue() as SecKey)
if status != 0 { println("status is: \(SecCopyErrorMessageString(status, &error).takeRetainedValue())") }
// creates SecTransform object using key
signer = SecSignTransformCreate(key, &error).takeRetainedValue()
if error == nil { println("signer transform creation error == nil") } else { println(error) }
// signer to get data from sourceData
SecTransformSetAttribute(signer, kSecTransformInputAttributeName, sourceData!, &error)
if error == nil { println("signer attribute setting error == nil") } else { println(error) }
// execute the transform
//signedData = (SecTransformExecute(signer, &error) as NSData)
let anything = SecTransformExecute(signer, &error)
if error == nil { println("signer execute error == nil") } else { println("erro: \(error!.takeRetainedValue())"); println(CFErrorGetCode(error!.takeRetainedValue())) }
println("anything = \(anything)")
//println(signedData)
I'm not very familiar with objc and actually not quite a proper coder, so please forgive my poor coding style ;) Also, sorry if I'm posting too much of it, but I figured better more than less...
Maybe I'm doing something wrong when transforming the String to binary for signing? I tried it both using CFData and NSData (to make this self contained, I'm using "Hello World" as my String, but in my code I actually load a UTF8 encoded XML from a file using NSData(contentsOfFile:) yet both generate the same error...)
Thanks you so much for your help! It's being a great learning experience, but I've been at it for over a week full-time now, so I really can use a break!! ;) :D
I have found a solution. The code no longer crashes, and I connected to the web service successfully after it, and the XMLDSIG signature was accepted by it (see related Question on XMLDSIG if interested in details on canonicalization and xml reference).
The key I was using is not compatible with signing (not sure why or even what the key was, actually...)
I was looking into counter-authenticating with a server using a X509 certificate (for an unrelated part of my solution) when I came across the SecIdentity class, needed to create a SecCredential together with the certificate and authenticate with the server.
I saw Identities embed a private key, and thought if could work for me. And it did!
Here are the changes I made:
Changed the kSecClass to kSecClassIdentity in the search dictionary
Retrieved the SecIdentity using SecItemCopyMatching
After casting it accordingly, used SecIdentityCopyPrivateKey to retrieve the private key into a SecKeyRef
Used this key in SecSignTransform, and voilà!! It worked!
Here is the working code:
// ...
// get the SecIdentity (substitutes KeySearchDict etc)
idSearchDict = [(kSecClass as String): (kSecClassIdentity as String), (kSecMatchSubjectContains as String): keyMatch, (kSecReturnRef as String):
status = SecItemCopyMatching(idSearchDict, &anyItem)
id = (anyItem!.takeRetainedValue() as SecIdentity)
// Retrieve the private key from SecIdentity
var KeyRef: Unmanaged<SecKeyRef>?
SecIdentityCopyPrivateKey(id, &KeyRef)
priKey = (KeyRef!.takeRetainedValue() as SecKey)
// Create SecSign using the private key
signer = SecSignTransformCreate(priKey, &error).takeRetainedValue()
if error != nil { print("signer transform creation error: ") ; println(error) }
/ signer to get data from sourceData
// ...
I'll post another question with the difficulties I'm facing with XMLDSIG, and add it to the comments, in case anyone is interested. I've already solved that too, and the answer is there in case you need it.
Thanks to everyone who tried to help, and hope this saves someone a lot of time and headache in the future!!
PS: loving Swift, otherwise 😉 😃

Cocoa Authorization in Swift

This is my first time writing in Swift, Cocoa (have experience in Cocoa Touch), and using Authorization, so I honestly have no idea if I am even on the right track. I am trying to make a modification to the hosts file, which requires user authentication, but both the AuthorizationCreate and AuthorizationExecuteWithPrivileges methods are giving errors.
var authorizationRef:AuthorizationRef
var status:OSStatus
status = AuthorizationCreate(nil, environment:kAuthorizationEmptyEnvironment, flags:kAuthorizationFlagDefaults, authorization:&authorizationRef)
let overwrite_hosts = "echo \(hostsContents) > /private/etc/hosts"
let args = [overwrite_hosts.cStringUsingEncoding(NSUTF8StringEncoding)]
status = AuthorizationExecuteWithPrivileges(authorizationRef, pathToTool:"/bin/sh", options:kAuthorizationFlagDefaults, arguments:args, communicationsPipe:nil)
Me calling AuthorizationCreate is throwing "Type '()' does not conform to protocol 'AuthorizationRef'" and my call of AuthorizationExecuteWithPrivileges is throwing "Could not find an overload for '__conversion' that accepts the supplied arguments"
Any ideas? Am I approaching this incorrectly?
Thanks for any help!
I was able to figure out how to do it via AppleScript, but you should be able to do it using the Authorization method I was trying before, therefore leaving this question open. Anybody looking for a quick solution (no error checks implemented) you can use what I wrote below:
func doScriptWithAdmin(inScript:String) -> String{
let script = "do shell script \"\(inScript)\" with administrator privileges"
var appleScript = NSAppleScript(source: script)
var eventResult = appleScript.executeAndReturnError(nil)
if !eventResult {
return "ERROR"
}else{
return eventResult.stringValue
}
}

Resources