Powerschool Swift 4 Xcode9 oauth issues - swift

Powerschool is a site that keeps track of letter days to indicate lab days for my high school. It also keeps track of people's grades, but I just want to scrape the letter day.
So I am trying to access info contained on html off the site https://pschool.princetonk12.org/guardian/home
To do that I must login through this portal https://pschool.princetonk12.org/public/
which uses oauth.
I know I have to use an authentication token and I see it printed in the output. How do I make a session on https://pschool.princetonk12.org/guardian/home to ultimately parse through it? How do I get the token and use it? Thanks so much!
Here is what I have so far:
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
let credential = URLCredential(user: "username", password: "password", persistence: URLCredential.Persistence.forSession)
let protectionSpace = URLProtectionSpace(host: "pschool.princetonk12.org", port: 443, protocol: "https", realm: "Restricted", authenticationMethod: NSURLAuthenticationMethodHTTPBasic)
URLCredentialStorage.shared.setDefaultCredential(credential, for: protectionSpace)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let url = URL(string: "https://pschool.princetonk12.org/public/.json")!
let task = session.dataTask(with: url) { (data, response, error) in
guard error == nil else {
print(error?.localizedDescription ?? "")
return
}
print(String(data: data!, encoding: .utf8))
}
task.resume()

Related

How can I pass data to my iOS app from a firebase cloud function without making a regular function call request?

I have a Firebase Cloud Function that I call from a URL rather than a function. The URL is used to load a WKWebView and the function is being called using one of the parameters in the URL, specifically the return_url.
An example of the URL to load the WKWebView would be https://domain.name?app_name=app_name&return_url=cloud_function_url.
private func loadWKWebView() {
let url = "https://domain.name"
let params = "param1=param1&return_url=\(cloud_function_url)"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "POST"
request.httpBody = params.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { (data : Data?, response : URLResponse?, error : Error?) in
if data != nil {
if let returnString = String(data: data!, encoding: .utf8) {
DispatchQueue.main.async {
self.webView.loadHTMLString(returnString, baseURL: URL(string: url)!)
}
}
}
}
task.resume()
}
The URL loads an authentication page in which the user must enter their username and password and returns the parameter I need.
I can console log the parameter but I don’t know how to pass the data to my iOS application because it is not a function “directly” that is making the call to the function expecting the result. The result depends on whether the user enters a valid username and password.
How can I send the response once the user logs in to my app?
What you are putting in params is query parameters. That should be appended to the URL, not sent as data in a POST.
I suggest using a URLComponents struct to compose your URL from the parts you need (probably host plus queryItems.)

How can I only allow LAN requests that aren't secure in Swift iOS14

I've been searching the internet and still cannot find an answer.
My app talks to other smart home products within the home. For example, it can make requests to the Philips Hue Bridge to control the lights via POST requests. The IP of my bridge is 192.168.0.12. I am making a POST request to this endpoint, however it isn't allowed as the connection is unsecure.
I still want to keep the setting of where external connections to domains are secure since I connect to my own server via a domain, which is secure. So I only want to allow local connections via local IP addresses to be unsecure.
I have tried this:
Yet it doesn't work. I've even tried using Allow Arbitrary Loads just for testing to see if it would work, and it still wouldn't.
My API call:
func getPhilipsHueUsername(completion: #escaping (String?, Error?) -> Void){
var bridgeIP = UserDefaults.standard.string(forKey: "bridgeIP")
let url = "http://" + bridgeIP! + "/api"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = HTTPMethod.post.rawValue
let body = [
"devicetype": "test"
]
do {
let dataToS = try JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
request.httpBody = dataToS
}catch{
print("Error creating data object")
return
}
AF.request(request).responseJSON { (response) in
switch response.result {
case .success(let value):
print(value)
if(response != nil){
let json = JSON(value)
print(value)
}
return completion("",nil)
case .failure(let error):
return completion(nil, error)
}
}
}

Upload file to S3 service 403 ERROR (com.amazonaws.AWSS3TransferUtilityErrorDomain error 2.) Swift iOS

I tried to upload my file to S3 service vie AWSS3 SDK swift.
My code:
let credentialsProvider = AWSStaticCredentialsProvider(accessKey: Config.main.accessKey, secretKey: Config.main.secretKey)
let configuration = AWSServiceConfiguration(region: .USEast1, endpoint: AWSEndpoint(url: URL(string: Config.main.AWS_ENDPOINT)!), credentialsProvider: credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
let image = UIImage(named: "photo")!
let data: Data = image.pngData()!
let remoteName = generateRandomStringWithLength(length: 12) + "." + data.format
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsURL.appendingPathComponent(remoteName)
try! data.write(to: fileURL, options: .atomic)
upload(fileUrl: fileURL, fileData: data, fileName: remoteName, type: .image, completionHandler: {_ in})
func upload(fileUrl: URL, fileData: Data, fileName: String, type: FileTypes, completionHandler: #escaping (URL?) -> ()) {
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = { task, progress in
DispatchQueue.main.async {
print("Progress = \(progress.completedUnitCount)/\(progress.totalUnitCount)")
}
}
let util = AWSS3TransferUtility.default()
util.uploadData(
fileData,
bucket: self.getBucket(type: type),
key: "\(self.getDir(type: type))_\(fileName)",
contentType: "image/png",
expression: expression) { task, error in
print("ERROR: \(error?.localizedDescription)")
print("response: \(task.response)")
print("response: \(task.response)")
}.continueWith { task in
if let error = task.error {
print("ERROR1: \(error.localizedDescription)")
}
return nil
}
}
It returns me something like 5 times progress response and after that
ERROR: Optional("The operation couldn’t be completed.
(com.amazonaws.AWSS3TransferUtilityErrorDomain error 2.)")
response: Optional( { URL:
http://(bucket).(host)/image_L24i8RGCeAaj.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=59589007eea780cf27c5%2F20200131%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20200131T125453Z&X-Amz-Expires=2999&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
} { Status Code: 403, Headers {
"Content-Length" = (
186
);
Date = (
"Fri, 31 Jan 2020 12:54:53 GMT"
);
Server = (
LeoFS
); } })
Android app with same setup works perfect.
What is the solution of the problem?
I was having same issue. Everything was working fine on android, While IOS was not working with same configuration.
Then I contacted AWS support, and they told me iPhones Date and time is wrong. I just adjusted date and time to correct date and time and then tried to upload and it worked.
hi if you are not using custom endpoints, change this line:
let configuration = AWSServiceConfiguration(region: .USEast1, endpoint: AWSEndpoint(url: URL(string: Config.main.AWS_ENDPOINT)!), credentialsProvider: credentialsProvider)
to:
let configuration = AWSServiceConfiguration(region: .USEast1, credentialsProvider: credentialsProvider)
and it will work just fine. The SDK can resolve the endpoint of the service by itself without providing an endpoint. You only need to provide an endpoint if you are using custom endpoints. It is also worth noting here that if you did want to pass the endpoint, you need to make sure you are passing the correct endpoint and us-east-1 for s3 has a special endpoint that does not include region as shown below:
let configuration = AWSServiceConfiguration(region: .USEast1,endpoint: AWSEndpoint(url: URL(string: "https://s3.amazonaws.com")) , credentialsProvider: credentialsProvider)
There is some useful info about this at https://medium.com/#lewisjkl/signing-aws4-31dcff1bf1f0, from Jeff Lewis.
It uses the CryptoSwift library; I'm working now to convert it to use the new CryptoKit.
Just check your device date and time. Because, AWS don't accept the wrong date or time.

Log into a site, and then download a file, in Swift

I'm downloading a CSV file from a website. I need to download this file while being logged in. The CSV file gives player projections for fantasy sports. When you download the file it will give you five players. However, if you purchase the premium service you get all player projections. I purchased the premium service, so, I'm trying to download this file while being signed into my account.
The code below downloads the CSV file with only five players. How do I sign into my account and then download this file?
guard let url = URL(string: "https://rotogrinders.com/projected-stats/nba-player.csv?site=fanduel") else { return }
let config = URLSessionConfiguration.default
// I don't know what I'm doing here. Also, the user name and password is not correct
let credential = URLCredential(user: "joe", password: "12345", persistence: .forSession)
let protectionSpace = URLProtectionSpace(host: "rotogrinders.com", port: 443, protocol: "https", realm: "Restricted", authenticationMethod: NSURLAuthenticationMethodHTTPBasic)
// I don't know what I'm doing here either.
let credentialStorage = URLCredentialStorage()
credentialStorage.set(credential, for: protectionSpace)
config.urlCredentialStorage = credentialStorage
let task = URLSession(configuration: config).dataTask(with: url) { data, response, error in
guard data != nil else { return }
guard let rows = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)?.components(separatedBy: "\n") else { return }
print(rows)
}
task.resume()
The problem is that this site doesn't have an API. They specifically don't allow the use case you are trying to do.
https://rotogrinders.com/threads/site-with-api-597932
However, there are great tools in Python that may let you do what you are trying to do. Take a look at scrapy:
https://www.edureka.co/blog/web-scraping-with-python/

Install TLS certificates on web server iOS app

I'm trying to integrate Swish payment in one of the apps I develop.
In order to be able to connect to the swish api I have to "set up TLS certificates from Swish Certificate Management and install it on "my" web server" according to the documentation.Here is the full technical documentation https://developer.getswish.se/merchants-api-manual/4-merchant-setup-process/.
The problem I don't understand is that I don't use a web server and I can't install those certificates there.
My app just offers some services for the client and after pressing the pay button should open the Swish app to finish the transaction in short.
What I tried is to make a post request to get the request token with which I can open the swish app loaded with the payment details.
I'm sure the problems are the certificates but couldn't find a good source explaining how to import(integrate) them.
let strURL = "https://mss.cpc.getswish.net/swish-cpcapi/api/v1/paymentrequests/"
guard let postURL = URL(string: strURL ) else {
print("Can't create url")
return
}
var request = URLRequest(url: postURL)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
let data: [String: Any] = [
"callbackUrl": "https://example.com/api/swishcb/paymentrequests",
"payeeAlias": "123xxxxxxx", // The Swish number of the payee. It needs to match with Merchant Swish number.
"amount": "100",
"currency": "SEK",
"message": "Test request to get the token"
]
do {
let jsonParams = try JSONSerialization.data(withJSONObject: data, options: [])
request.httpBody = jsonParams
} catch {
print("Error serializing the parameters of the post request")
return
}
// response will contain a Token, unique for each payment request
let config = URLSessionConfiguration.default
config.timeoutIntervalForResource = 120
config.timeoutIntervalForRequest = 120
let session = URLSession(configuration: config)
session.dataTask(with: request) { (data, response, error) in
print("Data \(data)")
print("Response \(response)")
if error != nil {
print("Error post request \(error?.localizedDescription)")
}
}.resume()
The error I got is:
Error post request Optional("An SSL error has occurred and a secure connection to the server cannot be made.")
018-12-21 12:24:55.549759+0200 tolk-24-7[7230:111102] [BoringSSL] boringssl_context_alert_callback_handler(3718) [C6.1:2][0x7fce4a77bf00] Alert level: fatal, description: handshake failure
2018-12-21 12:24:55.550047+0200 tolk-24-7[7230:111102] [BoringSSL] boringssl_session_errorlog(224) [C6.1:2][0x7fce4a77bf00] [boringssl_session_handshake_incomplete] SSL_ERROR_SSL(1): operation failed within the library
2018-12-21 12:24:55.550332+0200 tolk-24-7[7230:111102] [BoringSSL] boringssl_session_handshake_error_print(205) [C6.1:2][0x7fce4a77bf00] 140523985879704:error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE:/BuildRoot/Library/Caches/com.apple.xbs/Sources/boringssl_Sim/boringssl-109.220.4/ssl/tls_record.cc:586:SSL alert number 40
2018-12-21 12:24:55.550585+0200 tolk-24-7[7230:111102] [BoringSSL] boringssl_context_get_error_code(3539) [C6.1:2][0x7fce4a77bf00] SSL_AD_HANDSHAKE_FAILURE
2018-12-21 12:24:55.552299+0200 tolk-24-7[7230:111102] TIC TCP Conn Failed [6:0x600002dd6c40]: 3:-9824 Err(-9824)
2018-12-21 12:24:55.555924+0200 tolk-24-7[7230:111102] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
2018-12-21 12:24:55.556052+0200 tolk-24-7[7230:111102] Task <7888D080-D175-4DBF-8F66-4183F0D653E6>.<1> HTTP load failed (error code: -1200 [3:-9824])
2018-12-21 12:24:55.556234+0200 tolk-24-7[7230:111613] Task <7888D080-D175-4DBF-8F66-4183F0D653E6>.<1> finished with error - code: -1200
I feel your frustrations, I haven't worked with the Swish API per se, but it looks like URLSession is failing to perform the client certificate request. The handshake is failing on that step.
There is an option to add a URLSessionDelegate to URLSession in order to handle authentication challenges such as ServerTrust and ClientCertificate. They are discussing it here:
Swift 3 UrlSession with client authentication by certificate
If you're able to create a p12/pfx with the client certificate and private key, you can use SecPKCS12Import to import it and use it for the URLCredential trust in the NSURLAuthenticationMethodClientCertificate received in URLSessionDelegate. Here's an implementation I wrote:
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
{
let authenticationMethod = challenge.protectionSpace.authenticationMethod
if authenticationMethod == NSURLAuthenticationMethodServerTrust {
//Handle server trust if necessary
} else if authenticationMethod == NSURLAuthenticationMethodClientCertificate {
if let clientCredential = try? getClientUrlCredential() {
completionHandler(.useCredential, clientCredential)
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
getClientUrlCredential function:
private func getClientUrlCredential() throws -> URLCredential {
let p12Data = getP12Data() //Get the data from the bundle if it's bundled in the app
let p12Key = getP12Key() //you need the key set for creating the p12/pfx
let certOptions: NSDictionary = [
kSecImportExportPassphrase as NSString : p12Key as NSString
]
// import certificate to read its entries
var items: CFArray?
let status = SecPKCS12Import(p12Data, certOptions, &items)
if status == errSecSuccess,
let items = items,
let dict = (items as Array).first as? [String: AnyObject],
let certChain = dict[kSecImportItemCertChain as String] as? [SecTrust] {
// Check if SecIdentityGetTypeID is present
guard let cfIdentity = dict[kSecImportItemIdentity as String] as CFTypeRef?,
CFGetTypeID(cfIdentity) == SecIdentityGetTypeID() else {
throw URLSessionPinningDelegateError.localClientCertificateError
}
let identity = dict[kSecImportItemIdentity as String] as! SecIdentity
return URLCredential(
identity: identity,
certificates: certChain,
persistence: .forSession
)
}
//Failed to read local certificate, throw error
throw URLSessionPinningDelegateError.localClientCertificateError
}
With a valid client certificate you should be able to fulfill the client hello and set up the TLS towards the server since this seems to be where you are failing right now. The SSL alert number 40 you're getting by BoringSSL suggests so to me at least.
Hope this points you in the right direction at least, happy to support further if needed.